diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b2b1f91..c5fdc27 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,13 +7,13 @@ on: branches: [ master ] jobs: - unix: + linux: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-latest, macos-latest ] - py: [ "2.7", "3.7", "3.8", "3.9", "3.10", "3.11" ] + os: [ ubuntu-latest ] + py: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] rust: [ "1.41.1", "stable", "nightly" ] steps: @@ -44,13 +44,50 @@ jobs: run: | make test extensions + macos: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ macos-latest ] + py: [ "3.11", "3.12" ] + rust: [ "1.54.0", "stable", "nightly" ] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Python ${{ matrix.py }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.py }} + - name: Setup Rust ${{ matrix.rust }} + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - name: Check versions and paths + run: | + python -V ; rustc -V + echo "PATH=$PATH" + echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + echo "LIBRARY_PATH=$LIBRARY_PATH" + PYTHON_LIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))") + echo "PYTHON_LIB=$PYTHON_LIB" + echo "LIBRARY_PATH=$LIBRARY_PATH:$PYTHON_LIB" >> "$GITHUB_ENV" + - name: Remove Cargo.lock + if: ${{ matrix.rust == 'stable' || matrix.rust == 'nightly' }} + run: | + rm Cargo.lock + - name: Build and test + run: | + make test extensions + windows: runs-on: ${{ matrix.os }} strategy: matrix: os: [ windows-latest ] - py: [ "2.7", "3.7", "3.8", "3.9", "3.10", "3.11"] + py: [ "3.7", "3.8", "3.9", "3.10", "3.11", "3.12" ] rust: [ "1.41.1", "stable" ] steps: diff --git a/README.md b/README.md index 61b4358..1d5df7d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ rust-cpython [![Build Status](https://travis-ci.org/dgrunwald/rust-cpython.svg?branch=master)](https://travis-ci.org/dgrunwald/rust-cpython) ==================== -Warning: this package is no longer actively maintained. Python 3.12 is not supported and likely will not be supported. +Warning: this package is no longer actively maintained. Please switch to [PyO3](https://github.com/PyO3/pyo3) instead. [Rust](http://www.rust-lang.org/) bindings for the [python](https://www.python.org/) interpreter. @@ -17,9 +17,9 @@ Python is licensed under the [Python License](https://docs.python.org/2/license. Supported Python versions: * Python 2.7 -* Python 3.7 to 3.11 +* Python 3.7 to 3.12 -Warning: this package is no longer actively maintained. Python 3.12 is not supported and likely will not be supported. +Warning: this package is no longer actively maintained. Please switch to [PyO3](https://github.com/PyO3/pyo3) instead. Requires Rust 1.41.1 or later. diff --git a/python3-sys/src/code.rs b/python3-sys/src/code.rs index 3b18d8e..dc57ce4 100644 --- a/python3-sys/src/code.rs +++ b/python3-sys/src/code.rs @@ -23,18 +23,23 @@ pub struct PyCodeObject { pub co_names: *mut PyObject, pub co_exceptiontable: *mut PyObject, pub co_flags: c_int, + #[cfg(not(Py_3_12))] pub co_warmup: c_short, - co_linearray_entry_size: c_short, pub co_argcount: c_int, pub co_posonlyargcount: c_int, pub co_kwonlyargcount: c_int, pub co_stacksize: c_int, pub co_firstlineno: c_int, pub co_nlocalsplus: c_int, + #[cfg(Py_3_12)] + pub co_framesize: c_int, pub co_nlocals: c_int, + #[cfg(not(Py_3_12))] pub co_nplaincellvars: c_int, pub co_ncellvars: c_int, pub co_nfreevars: c_int, + #[cfg(Py_3_12)] + pub co_version: u32, pub co_localsplusnames: *mut PyObject, pub co_localspluskinds: *mut PyObject, pub co_filename: *mut PyObject, @@ -147,6 +152,7 @@ pub const CO_MAXBLOCKS: usize = 20; extern "C" { pub static mut PyCode_Type: PyTypeObject; + #[cfg_attr(Py_3_12, link_name = "PyUnstable_Code_New")] pub fn PyCode_New( argcount: c_int, kwonlyargcount: c_int, @@ -170,6 +176,7 @@ extern "C" { ) -> *mut PyCodeObject; #[cfg(Py_3_8)] + #[cfg_attr(Py_3_12, link_name = "PyUnstable_Code_NewWithPosOnlyArgs")] pub fn PyCode_NewWithPosOnlyArgs( argcount: c_int, posonlyargcount: c_int, diff --git a/python3-sys/src/compile.rs b/python3-sys/src/compile.rs index 2b1888b..63d3418 100644 --- a/python3-sys/src/compile.rs +++ b/python3-sys/src/compile.rs @@ -7,12 +7,25 @@ use crate::object::PyObject; use crate::pyarena::*; use crate::pythonrun::*; +#[repr(C)] +#[derive(Copy, Clone)] +#[cfg(Py_3_12)] +pub struct _PyCompilerSrcLocation { + pub lineno: c_int, + pub end_lineno: c_int, + pub col_offset: c_int, + pub end_col_offset: c_int, +} + #[repr(C)] #[derive(Copy, Clone)] #[cfg(not(Py_LIMITED_API))] pub struct PyFutureFeatures { pub ff_features: c_int, + #[cfg(not(Py_3_12))] pub ff_lineno: c_int, + #[cfg(Py_3_12)] + pub ff_location: _PyCompilerSrcLocation, } // TODO: PyCF_MASK etc. constants diff --git a/python3-sys/src/initconfig.rs b/python3-sys/src/initconfig.rs index 24f1011..b4813ec 100644 --- a/python3-sys/src/initconfig.rs +++ b/python3-sys/src/initconfig.rs @@ -107,6 +107,8 @@ pub struct PyConfig { #[cfg(all(Py_3_9, not(Py_3_10)))] pub _use_peg_parser: c_int, pub tracemalloc: c_int, + #[cfg(Py_3_12)] + pub perf_profiling: c_int, pub import_time: c_int, #[cfg(Py_3_11)] pub code_debug_ranges: c_int, @@ -151,6 +153,8 @@ pub struct PyConfig { pub use_frozen_modules: c_int, #[cfg(Py_3_11)] pub safe_path: c_int, + #[cfg(Py_3_12)] + pub int_max_str_digits: c_int, // Path configuration inputs: pub pathconfig_warnings: c_int, #[cfg(Py_3_10)] @@ -180,12 +184,12 @@ pub struct PyConfig { // Private fields pub _install_importlib: c_int, pub _init_main: c_int, - #[cfg(Py_3_9)] + #[cfg(all(Py_3_9, not(Py_3_12)))] pub _isolated_interpreter: c_int, - #[cfg(all(Py_3_9, not(Py_3_10)))] - pub _orig_argv: PyWideStringList, #[cfg(Py_3_11)] pub _is_python_build: c_int, + #[cfg(all(Py_3_9, not(Py_3_10)))] + pub _orig_argv: PyWideStringList, } impl Default for PyConfig { diff --git a/python3-sys/src/modsupport.rs b/python3-sys/src/modsupport.rs index 64cb5be..fa02ec9 100644 --- a/python3-sys/src/modsupport.rs +++ b/python3-sys/src/modsupport.rs @@ -133,7 +133,7 @@ pub unsafe fn PyModule_FromDefAndSpec(def: *mut PyModuleDef, spec: *mut PyObject ) } -#[cfg(not(Py_LIMITED_API))] +#[cfg(all(not(Py_LIMITED_API), not(Py_3_12)))] #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { pub static mut _Py_PackageContext: *const c_char; diff --git a/python3-sys/src/object.rs b/python3-sys/src/object.rs index 40f1a85..40042b0 100644 --- a/python3-sys/src/object.rs +++ b/python3-sys/src/object.rs @@ -436,6 +436,8 @@ mod typeobject { pub tp_finalize: Option, #[cfg(Py_3_8)] pub tp_vectorcall: Option, + #[cfg(Py_3_12)] + pub tp_watched: c_char, #[cfg(all(Py_3_8, not(Py_3_9)))] pub tp_print: Option, #[cfg(all(py_sys_config = "COUNT_ALLOCS", not(Py_3_9)))] @@ -535,7 +537,16 @@ mod typeobject { } } - #[cfg(Py_3_9)] + #[cfg(Py_3_12)] + pub const PyTypeObject_INIT: PyTypeObject = py_type_object_init_with_count_allocs!( + tp_as_async: 0 as *mut PyAsyncMethods, + tp_vectorcall_offset: 0, + tp_vectorcall: None, + tp_finalize: None, + tp_watched: 0, + ); + + #[cfg(all(Py_3_9, not(Py_3_12)))] pub const PyTypeObject_INIT: PyTypeObject = py_type_object_init_with_count_allocs!( tp_as_async: 0 as *mut PyAsyncMethods, tp_vectorcall_offset: 0, diff --git a/python3-sys/src/objimpl.rs b/python3-sys/src/objimpl.rs index a731a54..5a8e302 100644 --- a/python3-sys/src/objimpl.rs +++ b/python3-sys/src/objimpl.rs @@ -21,7 +21,7 @@ extern "C" { #[cfg(all(py_sys_config = "Py_DEBUG", not(Py_3_4)))] pub fn _PyObject_DebugFree(arg1: *mut c_void); - #[cfg(all(not(Py_LIMITED_API), Py_3_4))] + #[cfg(all(not(Py_LIMITED_API), Py_3_4, not(Py_3_11)))] pub fn _Py_GetAllocatedBlocks() -> Py_ssize_t; pub fn PyObject_Init(arg1: *mut PyObject, arg2: *mut PyTypeObject) -> *mut PyObject; pub fn PyObject_InitVar( diff --git a/python3-sys/src/unicodeobject.rs b/python3-sys/src/unicodeobject.rs index 310d956..a0556b7 100644 --- a/python3-sys/src/unicodeobject.rs +++ b/python3-sys/src/unicodeobject.rs @@ -52,7 +52,7 @@ extern "C" { length: Py_ssize_t, fill_char: Py_UCS4, ) -> Py_ssize_t; - #[cfg(not(Py_LIMITED_API))] + #[cfg(all(not(Py_LIMITED_API), not(Py_3_12)))] #[deprecated(since = "0.2.1", note = "Deprecated since Python 3.3 / PEP 393")] pub fn PyUnicode_FromUnicode(u: *const Py_UNICODE, size: Py_ssize_t) -> *mut PyObject; @@ -78,10 +78,10 @@ extern "C" { copy_null: c_int, ) -> *mut Py_UCS4; pub fn PyUnicode_AsUCS4Copy(unicode: *mut PyObject) -> *mut Py_UCS4; - #[cfg(not(Py_LIMITED_API))] + #[cfg(all(not(Py_LIMITED_API), not(Py_3_12)))] #[deprecated(since = "0.2.1", note = "Deprecated since Python 3.3 / PEP 393")] pub fn PyUnicode_AsUnicode(unicode: *mut PyObject) -> *mut Py_UNICODE; - #[cfg(not(Py_LIMITED_API))] + #[cfg(all(not(Py_LIMITED_API), not(Py_3_12)))] #[deprecated(since = "0.2.1", note = "Deprecated since Python 3.3 / PEP 393")] pub fn PyUnicode_AsUnicodeAndSize( unicode: *mut PyObject, @@ -435,7 +435,7 @@ extern "C" { #[deprecated(since = "0.6.1", note = "Deprecated since Python 3.3; removed in 3.10")] pub fn PyUnicode_AsUnicodeCopy(unicode: *mut PyObject) -> *mut Py_UNICODE; - #[cfg(not(Py_LIMITED_API))] + #[cfg(not(any(Py_LIMITED_API, Py_3_12)))] fn _PyUnicode_Ready(o: *mut PyObject) -> c_int; } @@ -446,6 +446,7 @@ pub struct PyASCIIObject { pub length: Py_ssize_t, pub hash: Py_hash_t, pub state: u32, + #[cfg(not(Py_3_12))] pub wstr: *mut c_void, } @@ -455,6 +456,7 @@ pub struct PyCompactUnicodeObject { _base: PyASCIIObject, utf8_length: Py_ssize_t, utf8: *mut u8, + #[cfg(not(Py_3_12))] wstr_length: Py_ssize_t, } @@ -494,6 +496,7 @@ pub const PyUnicode_4BYTE_KIND: u32 = 4; #[inline] pub unsafe fn PyUnicode_KIND(o: *mut PyObject) -> u32 { debug_assert!(PyUnicode_Check(o) > 0); + #[cfg(not(Py_3_12))] debug_assert!(PyUnicode_IS_READY(o)); let state = (*(o as *mut PyASCIIObject)).state; (state >> 2) & 7 @@ -502,6 +505,7 @@ pub unsafe fn PyUnicode_KIND(o: *mut PyObject) -> u32 { #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyUnicode_DATA(o: *mut PyObject) -> *mut c_void { debug_assert!(PyUnicode_Check(o) > 0); + #[cfg(not(Py_3_12))] debug_assert!(PyUnicode_IS_READY(o)); if PyUnicode_IS_COMPACT(o) { // fn _PyUnicode_COMPACT_DATA @@ -522,11 +526,12 @@ pub unsafe fn PyUnicode_DATA(o: *mut PyObject) -> *mut c_void { #[inline] pub unsafe fn PyUnicode_GET_LENGTH(o: *mut PyObject) -> Py_ssize_t { debug_assert!(PyUnicode_Check(o) > 0); + #[cfg(not(Py_3_12))] debug_assert!(PyUnicode_IS_READY(o)); (*(o as *mut PyASCIIObject)).length } -#[cfg(not(Py_LIMITED_API))] +#[cfg(not(any(Py_LIMITED_API, Py_3_12)))] #[inline] unsafe fn PyUnicode_IS_READY(o: *mut PyObject) -> bool { let ready_bit = 1 << 7; @@ -538,9 +543,16 @@ unsafe fn PyUnicode_IS_READY(o: *mut PyObject) -> bool { #[inline] pub unsafe fn PyUnicode_READY(o: *mut PyObject) -> c_int { debug_assert!(PyUnicode_Check(o) > 0); - if PyUnicode_IS_READY(o) { - 0 - } else { - _PyUnicode_Ready(o) + #[cfg(Py_3_12)] + { + return 0; + } + #[cfg(not(Py_3_12))] + { + if PyUnicode_IS_READY(o) { + 0 + } else { + _PyUnicode_Ready(o) + } } } diff --git a/tests/check_symbols.py b/tests/check_symbols.py index b5746fd..e13f674 100755 --- a/tests/check_symbols.py +++ b/tests/check_symbols.py @@ -89,12 +89,19 @@ def match_braces(text): foreign_sections.append(asttree[:endpos]) asttree = asttree[endpos:] +renames = {} +if sys.version_info >= (3, 12): + # Those renames were declared by cfg_attr(Py_3_12, link_name = ...) but it's hard to parse them from AST. + renames.update({"PyCode_New": "PyUnstable_Code_New", "PyCode_NewWithPosOnlyArgs": "PyUnstable_Code_NewWithPosOnlyArgs"}) + for section in foreign_sections: lines = section.split('\n') for idx in range(len(lines)): line = lines[idx] if ('kind: Fn(' in line) or ('kind: Static(' in line): - foreign_symbols.add(re.sub(r'\s*ident: (.*)#[0-9]*,', r'\1', lines[idx-1])) + name = re.sub(r'\s*ident: (.*)#[0-9]*,', r'\1', lines[idx-1]) + name = renames.get(name) or name + foreign_symbols.add(name) assert 'PyList_Type' in foreign_symbols, "Failed getting statics from rustc -Z unpretty=ast-tree,expanded" assert 'PyList_New' in foreign_symbols, "Failed getting functions from rustc -Z unpretty=ast-tree,expanded"