From a1fae2911d871d93e50acfbb297c93a7a6ba04d9 Mon Sep 17 00:00:00 2001 From: Arie Bovenberg Date: Fri, 2 Aug 2024 10:15:51 +0200 Subject: [PATCH] add python 3.13 wheels, now that its ABI is stable --- .github/workflows/checks.yml | 4 ++ .github/workflows/wheels.yml | 6 +- CHANGELOG.rst | 6 ++ pyproject.toml | 2 +- pysrc/whenever/_pywhenever.py | 2 +- src/lib.rs | 100 ++++++++++++++++------------------ 6 files changed, 63 insertions(+), 57 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3f575c62..4519a1c6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -123,6 +123,10 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.12' + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: "1.79" + components: "clippy, rustfmt" - run: | pip install . pip install -U pip diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index f809884f..ec0ecf62 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -61,9 +61,11 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 + if : ${{ matrix.os == 'windows' }} with: - python-version: '3.12' + python-version: '3.13' architecture: ${{ matrix.python-architecture || 'x64' }} + allow-prereleases: true - run: pip install -U twine - name: Generate third-party license information run: | @@ -73,7 +75,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --strip --out dist --interpreter '3.9 3.10 3.11 3.12' + args: --release --strip --out dist --interpreter '3.9 3.10 3.11 3.12 3.13' manylinux: ${{ matrix.manylinux || 'auto' }} sccache: 'true' rust-toolchain: "1.79" diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eaf3900b..5383d7de 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ 🚀 Changelog ============ +0.6.7 (2024-08-02) +------------------ + +- Add Python 3.13 binary wheels, now that its ABI is stable +- Small improvements to import speed + 0.6.6 (2024-07-27) ------------------ diff --git a/pyproject.toml b/pyproject.toml index 3fd62b23..c060aa8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ authors = [ {name = "Arie Bovenberg", email = "a.c.bovenberg@gmail.com"}, ] readme = "README.md" -version = "0.6.6" +version = "0.6.7rc0" description = "Modern datetime library for Python, written in Rust" requires-python = ">=3.9" classifiers = [ diff --git a/pysrc/whenever/_pywhenever.py b/pysrc/whenever/_pywhenever.py index a61578ac..63e7cd99 100644 --- a/pysrc/whenever/_pywhenever.py +++ b/pysrc/whenever/_pywhenever.py @@ -32,7 +32,7 @@ # - It saves some overhead from __future__ import annotations -__version__ = "0.6.6" +__version__ = "0.6.7rc0" import enum import re diff --git a/src/lib.rs b/src/lib.rs index d623c7f2..8de107f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,11 +204,7 @@ unsafe fn create_enum(name: &CStr, members: &[(&CStr, i32)]) -> PyReturn { unsafe fn new_exc(module: *mut PyObject, name: &CStr, base: *mut PyObject) -> *mut PyObject { let e = PyErr_NewException(name.as_ptr(), base, NULL()); - if e.is_null() { - return NULL(); - } - defer_decref!(e); - if PyModule_AddType(module, e.cast()) != 0 { + if e.is_null() || PyModule_AddType(module, e.cast()) != 0 { return NULL(); } e @@ -239,9 +235,8 @@ unsafe fn new_type( let Some(pyvalue) = value.to_obj(cls).ok() else { return false; }; - // NOTE: we don't decref the value here on purpose. - // Singletons work out refcount/GC-wise in the end. - if PyDict_SetItemString((*cls).tp_dict, name.as_ptr().cast(), pyvalue) != 0 { + defer_decref!(pyvalue); + if PyDict_SetItemString((*cls).tp_dict, name.as_ptr(), pyvalue) != 0 { return false; } } @@ -440,46 +435,45 @@ unsafe extern "C" fn module_exec(module: *mut PyObject) -> c_int { // Making time patcheable results in a performance hit. // Only enable it if the time_machine module is available. - state.time_machine_exists = match PyImport_ImportModule(c"time_machine".as_ptr()).as_result() { - Ok(m) => { - Py_DecRef(m); - true - } - Err(_) => { - PyErr_Clear(); - false - } - }; - + state.time_machine_exists = unwrap_or_errcode!(time_machine_installed()); state.time_patch = TimePatch::Unset; 0 } -unsafe fn do_visit(target: *mut PyObject, visit: visitproc, arg: *mut c_void) { - let obj: *mut PyObject = target.cast(); - if !obj.is_null() { - (visit)(obj, arg); +unsafe fn time_machine_installed() -> PyResult { + // Important: we don't import the `time_machine` here, + // because that would be slower. We only need to check its existence. + let find_spec = PyObject_GetAttrString( + steal!(PyImport_ImportModule(c"importlib.util".as_ptr()).as_result()?), + c"find_spec".as_ptr(), + ); + defer_decref!(find_spec); + let spec = call1(find_spec, steal!("time_machine".to_py()?))?; + defer_decref!(spec); + Ok((spec as *mut PyObject) != Py_None()) +} + +unsafe fn traverse(target: *mut PyObject, visit: visitproc, arg: *mut c_void) { + if !target.is_null() { + (visit)(target, arg); } } -unsafe fn do_type_visit( +unsafe fn traverse_type( target: *mut PyTypeObject, visit: visitproc, arg: *mut c_void, num_singletons: usize, ) { - let obj: *mut PyObject = target.cast(); - if !obj.is_null() { - (visit)(obj, arg); - // XXX: This trick SEEMS to let us avoid adding GC - // support to our types: Since our types are atomic and immutable - // this should be allowed... + if !target.is_null() { + // XXX: This trick SEEMS to let us avoid adding GC support to our types. + // Since our types are atomic and immutable this should be allowed... // ...BUT there is a reference cycle between the class and the // singleton instances (e.g. the Date.MAX instance and Date class itself) // Visiting the type once for each singleton should make GC aware of this. - for _ in 0..num_singletons { - (visit)(obj, arg); + for _ in 0..(num_singletons + 1) { + (visit)(target.cast(), arg); } } } @@ -491,46 +485,46 @@ unsafe extern "C" fn module_traverse( ) -> c_int { let state = State::for_mod(module); // types - do_type_visit(state.date_type, visit, arg, date::SINGLETONS.len()); - do_type_visit(state.time_type, visit, arg, time::SINGLETONS.len()); - do_type_visit( + traverse_type(state.date_type, visit, arg, date::SINGLETONS.len()); + traverse_type(state.time_type, visit, arg, time::SINGLETONS.len()); + traverse_type( state.date_delta_type, visit, arg, date_delta::SINGLETONS.len(), ); - do_type_visit( + traverse_type( state.time_delta_type, visit, arg, time_delta::SINGLETONS.len(), ); - do_type_visit( + traverse_type( state.datetime_delta_type, visit, arg, datetime_delta::SINGLETONS.len(), ); - do_type_visit( + traverse_type( state.local_datetime_type, visit, arg, local_datetime::SINGLETONS.len(), ); - do_type_visit(state.instant_type, visit, arg, instant::SINGLETONS.len()); - do_type_visit( + traverse_type(state.instant_type, visit, arg, instant::SINGLETONS.len()); + traverse_type( state.offset_datetime_type, visit, arg, offset_datetime::SINGLETONS.len(), ); - do_type_visit( + traverse_type( state.zoned_datetime_type, visit, arg, zoned_datetime::SINGLETONS.len(), ); - do_type_visit( + traverse_type( state.system_datetime_type, visit, arg, @@ -539,22 +533,22 @@ unsafe extern "C" fn module_traverse( // enum members for &member in state.weekday_enum_members.iter() { - do_visit(member, visit, arg); + traverse(member, visit, arg); } // exceptions - do_visit(state.exc_repeated, visit, arg); - do_visit(state.exc_skipped, visit, arg); - do_visit(state.exc_invalid_offset, visit, arg); - do_visit(state.exc_implicitly_ignoring_dst, visit, arg); + traverse(state.exc_repeated, visit, arg); + traverse(state.exc_skipped, visit, arg); + traverse(state.exc_invalid_offset, visit, arg); + traverse(state.exc_implicitly_ignoring_dst, visit, arg); // Imported modules - do_visit(state.zoneinfo_type, visit, arg); - do_visit(state.timezone_type, visit, arg); - do_visit(state.strptime, visit, arg); - do_visit(state.format_rfc2822, visit, arg); - do_visit(state.parse_rfc2822, visit, arg); - do_visit(state.time_ns, visit, arg); + traverse(state.zoneinfo_type, visit, arg); + traverse(state.timezone_type, visit, arg); + traverse(state.strptime, visit, arg); + traverse(state.format_rfc2822, visit, arg); + traverse(state.parse_rfc2822, visit, arg); + traverse(state.time_ns, visit, arg); 0 }