diff --git a/.gitattributes b/.gitattributes index 650a5619..66bcdac6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -include linguist-vendored +include/pyo3/pyo3-build-config/**/* linguist-vendored +include/pyo3/pyo3-ffi/**/* linguist-vendored +include/yyjson/* linguist-vendored diff --git a/.github/workflows/artifact.yaml b/.github/workflows/artifact.yaml index ecb0cd86..29ea79a8 100644 --- a/.github/workflows/artifact.yaml +++ b/.github/workflows/artifact.yaml @@ -40,7 +40,7 @@ jobs: - run: python3 -m pip install --user -r test/requirements.txt -r integration/requirements.txt mypy - - run: pytest -s -rxX -v -n 4 test + - run: pytest -v test env: PYTHONMALLOC: "debug" @@ -58,31 +58,30 @@ jobs: overwrite: true retention-days: 1 - manylinux_2_17_amd64: + manylinux_amd64: runs-on: ubuntu-24.04 timeout-minutes: 10 strategy: fail-fast: false matrix: + python: [ + { interpreter: 'python3.13', package: 'python3.13', compatibility: "manylinux_2_17" }, + { interpreter: 'python3.12', package: 'python3.12', compatibility: "manylinux_2_17" }, + { interpreter: 'python3.11', package: 'python3.11', compatibility: "manylinux_2_17" }, + { interpreter: 'python3.10', package: 'python3.10', compatibility: "manylinux_2_17" }, + { interpreter: 'python3.9', package: 'python3.9', compatibility: "manylinux_2_17" }, + ] arch: [ { cc: "clang", cflags: "-Os -fstrict-aliasing -fno-plt -flto=full -emit-llvm", features: "avx512,no-panic,unstable-simd,yyjson", ldflags: "-fuse-ld=lld -Wl,-plugin-opt=also-emit-llvm -Wl,--as-needed -Wl,-zrelro,-znow", - rustflags: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=4 -D warnings", + rustflags: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z threads=4 -D warnings", tag: null, target: "x86_64-unknown-linux-gnu", }, ] - python: [ - { interpreter: 'python3.13', package: 'python3.13' }, - { interpreter: 'python3.12', package: 'python3.12' }, - { interpreter: 'python3.11', package: 'python3.11' }, - { interpreter: 'python3.10', package: 'python3.10' }, - { interpreter: 'python3.9', package: 'python3.9' }, - { interpreter: 'python3.8', package: 'python3.8' }, - ] env: PYTHON: "${{ matrix.python.interpreter }}" PYTHON_PACKAGE: "${{ matrix.python.package }}" @@ -94,6 +93,7 @@ jobs: LDFLAGS: "${{ matrix.arch.ldflags }}" RUSTFLAGS: "${{ matrix.arch.rustflags }}" CARGO_TARGET_DIR: "/tmp/orjson" + COMPATIBILITY: "${{ matrix.python.compatibility }}" steps: - name: cpuinfo @@ -106,6 +106,7 @@ jobs: with: container: registry.fedoraproject.org/fedora:42 arch: ${{ matrix.arch.tag }} + initial_delay: 5s podman_args: "-v .:/orjson -v /tmp:/tmp --workdir /orjson" - name: setup-shell-wrapper @@ -128,13 +129,13 @@ jobs: maturin build --release --strip \ --features="${FEATURES}" \ - --compatibility=manylinux_2_17 \ + --compatibility="${COMPATIBILITY}" \ --interpreter="${PYTHON}" \ --target="${TARGET}" uv pip install ${CARGO_TARGET_DIR}/wheels/orjson*.whl - pytest -s -rxX -v -n 4 test + pytest -v test ./integration/run thread ./integration/run http ./integration/run init @@ -151,26 +152,26 @@ jobs: retention-days: 1 - manylinux_2_17_aarch64: + manylinux_aarch64: runs-on: ubuntu-24.04 timeout-minutes: 45 strategy: fail-fast: false matrix: + python: [ + { interpreter: 'python3.13', package: 'python3.13', compatibility: "manylinux_2_17" }, + ] arch: [ { cc: "clang", cflags: "-Os -fstrict-aliasing -fno-plt -flto=full -emit-llvm", features: "no-panic,unstable-simd,yyjson", ldflags: "-fuse-ld=lld -Wl,-plugin-opt=also-emit-llvm -Wl,--as-needed -Wl,-zrelro,-znow", - rustflags: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=4 -D warnings", + rustflags: "-C linker=clang -C link-arg=-fuse-ld=lld -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z threads=4 -D warnings", tag: "aarch64", target: "aarch64-unknown-linux-gnu", }, ] - python: [ - { interpreter: 'python3.13', package: 'python3.13' }, - ] env: PYTHON: "${{ matrix.python.interpreter }}" PYTHON_PACKAGE: "${{ matrix.python.package }}" @@ -182,6 +183,7 @@ jobs: LDFLAGS: "${{ matrix.arch.ldflags }}" RUSTFLAGS: "${{ matrix.arch.rustflags }}" CARGO_TARGET_DIR: "/tmp/orjson" + COMPATIBILITY: "${{ matrix.python.compatibility }}" steps: - name: cpuinfo @@ -212,18 +214,17 @@ jobs: ./script/install-fedora - source "${HOME}/.cargo/env" source "${VENV}/bin/activate" maturin build --release --strip \ --features="${FEATURES}" \ - --compatibility=manylinux_2_17 \ + --compatibility="${COMPATIBILITY}" \ --interpreter="${PYTHON}" \ --target="${TARGET}" uv pip install ${CARGO_TARGET_DIR}/wheels/orjson*.whl - pytest -s -rxX -v -n 2 test + pytest -v test cp ${CARGO_TARGET_DIR}/wheels/orjson*.whl dist @@ -231,12 +232,12 @@ jobs: if: "startsWith(github.ref, 'refs/tags/')" uses: actions/upload-artifact@v4 with: - name: orjson_manylinux_2_17_aarch64_${{ matrix.python.interpreter }} + name: orjson_manylinux_aarch64_${{ matrix.python.interpreter }} path: dist overwrite: true retention-days: 1 - musllinux_1_2: + musllinux: runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -272,7 +273,7 @@ jobs: CC: "gcc" CFLAGS: "-Os" LDFLAGS: "-Wl,--as-needed" - RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=2 -D warnings -C target-feature=-crt-static" + RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z threads=2 -D warnings -C target-feature=-crt-static" with: rust-toolchain: "${{ env.RUST_TOOLCHAIN }}" rustup-components: rust-src @@ -301,18 +302,18 @@ jobs: venv/bin/pip install -U pip wheel venv/bin/pip install -r test/requirements.txt venv/bin/pip install orjson --no-index --find-links dist/ --force-reinstall - venv/bin/python -m pytest -s -rxX -v -n 2 test + venv/bin/python -m pytest -v test - name: Store wheels if: "startsWith(github.ref, 'refs/tags/')" uses: actions/upload-artifact@v4 with: - name: orjson_musllinux_1_2_${{ matrix.platform.arch }}_${{ matrix.python.version }} + name: orjson_musllinux_${{ matrix.platform.arch }}_${{ matrix.python.version }} path: dist overwrite: true retention-days: 1 - manylinux_2_17_non_amd64: + manylinux_non_amd64: runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -385,7 +386,7 @@ jobs: overwrite: true retention-days: 1 - macos_universal2_aarch64: + macos_aarch64: runs-on: macos-14 strategy: fail-fast: false @@ -402,7 +403,7 @@ jobs: LDFLAGS: "-Wl,--as-needed" CFLAGS_x86_64_apple_darwin: "-O2 -fstrict-aliasing -flto=full -march=x86-64-v2 -mtune=generic" CFLAGS_aarch64_apple_darwin: "-O2 -fstrict-aliasing -flto=full -mcpu=apple-m1 -mtune=generic" - RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=3 -D warnings" + RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z threads=3 -D warnings" PATH: "/Users/runner/work/orjson/orjson/.venv/bin:/Users/runner/.cargo/bin:/usr/local/opt/curl/bin:/usr/local/bin:/usr/local/sbin:/Users/runner/bin:/Library/Frameworks/Python.framework/Versions/Current/bin:/usr/bin:/bin:/usr/sbin:/sbin" steps: @@ -441,7 +442,7 @@ jobs: --target=universal2-apple-darwin uv pip install target/wheels/orjson*.whl - - run: pytest -s -rxX -v -n 3 test + - run: pytest -v test env: PYTHONMALLOC: "debug" @@ -458,7 +459,7 @@ jobs: overwrite: true retention-days: 1 - macos_universal2_amd64: + macos_amd64: runs-on: macos-13 strategy: fail-fast: false @@ -473,7 +474,7 @@ jobs: LDFLAGS: "-Wl,--as-needed" CFLAGS_x86_64_apple_darwin: "-O2 -fstrict-aliasing -flto=full -march=x86-64-v2 -mtune=generic" CFLAGS_aarch64_apple_darwin: "-O2 -fstrict-aliasing -flto=full -mcpu=apple-m1 -mtune=generic" - RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=3 -D warnings" + RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z threads=3 -D warnings" PATH: "/Users/runner/work/orjson/orjson/.venv/bin:/Users/runner/.cargo/bin:/usr/local/opt/curl/bin:/usr/local/bin:/usr/local/sbin:/Users/runner/bin:/Library/Frameworks/Python.framework/Versions/Current/bin:/usr/bin:/bin:/usr/sbin:/sbin" steps: @@ -511,7 +512,7 @@ jobs: --target=universal2-apple-darwin uv pip install target/wheels/orjson*.whl - - run: pytest -s -rxX -v -n 3 test + - run: pytest -v test env: PYTHONMALLOC: "debug" @@ -533,29 +534,39 @@ jobs: runs-on: ubuntu-24.04 if: "startsWith(github.ref, 'refs/tags/')" needs: [ - macos_universal2_aarch64, - macos_universal2_amd64, - manylinux_2_17_aarch64, - manylinux_2_17_amd64, - manylinux_2_17_non_amd64, - musllinux_1_2, + macos_aarch64, + macos_amd64, + manylinux_aarch64, + manylinux_amd64, + manylinux_non_amd64, + musllinux, sdist, ] + environment: + name: pypi + url: https://pypi.org/p/orjson + permissions: + id-token: write steps: - uses: actions/download-artifact@v4 with: - pattern: orjson_* merge-multiple: true + name: python-package-distributions + path: dist/ + pattern: orjson_* + + - run: ls -1 dist/ + - uses: actions/setup-python@v5 with: python-version: "3.12" - - run: pip install pip "maturin>=1,<2" - - run: ls -1 . - - name: deploy wheel - run: maturin upload --skip-existing --username "$MATURIN_USERNAME" *.whl - env: - MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - - name: deploy sdist - run: maturin upload --skip-existing --username "$MATURIN_USERNAME" *.tar.gz - env: - MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + attestations: true + packages-dir: dist + repository-url: https://github.com/ijl/orjson + skip-existing: true + user: ijl + verbose: true diff --git a/.github/workflows/debug.yaml b/.github/workflows/debug.yaml index 60c9fe05..863135ac 100644 --- a/.github/workflows/debug.yaml +++ b/.github/workflows/debug.yaml @@ -10,8 +10,8 @@ jobs: profile: [ { rust: "1.72", features: "" }, { rust: "1.72", features: "--features=yyjson" }, - { rust: "nightly-2024-09-25", features: "--features=yyjson,unstable-simd"}, - { rust: "nightly-2024-09-25", features: "--features=avx512,yyjson,unstable-simd"}, + { rust: "nightly-2024-09-25", features: "--features=yyjson,unstable-simd" }, + { rust: "nightly-2024-09-25", features: "--features=avx512,yyjson,unstable-simd" }, ] python: [ { version: '3.13' }, @@ -27,12 +27,8 @@ jobs: steps: - run: curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain ${{ matrix.profile.rust }} --profile minimal -y - - name: cpuinfo - run: cat /proc/cpuinfo - - uses: actions/setup-python@v5 with: - allow-prereleases: true python-version: '${{ matrix.python.version }}' - run: python -m pip install --user --upgrade pip "maturin>=1,<2" wheel diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index df817411..e97ed4b5 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -8,7 +8,7 @@ jobs: with: python-version: "3.12" - run: curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain=stable --profile=default -y - - run: pip install -U autoflake isort black ruff mypy types-python-dateutil types-pytz types-simplejson types-ujson + - run: pip install -U ruff==0.7.1 mypy==1.13.0 - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9688cd4c..f8a740e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog -### 3.10.10 +## 3.10.10 ### Fixed @@ -12,7 +12,7 @@ - Publish aarch64 manylinux_2_17 wheel for 3.13 to PyPI. -### 3.10.9 +## 3.10.9 ### Fixed @@ -20,7 +20,7 @@ introduced in 3.10.8. -### 3.10.8 +## 3.10.8 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 029b5505..fc7f72bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,9 +61,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -101,9 +101,9 @@ checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" [[package]] name = "jiff" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a45489186a6123c128fdf6016183fcfab7113e1820eb813127e036e287233fb" +checksum = "b9d9d414fc817d3e3d62b2598616733f76c4cc74fbac96069674739b881295c8" [[package]] name = "libc" @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -212,18 +212,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.211" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac55e59090389fb9f0dd9e0f3c09615afed1d19094284d0b200441f13550793" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.211" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be4f245ce16bc58d57ef2716271d0d4519e0f6defa147f6e081005bcb278ff" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -268,9 +268,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.82" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -296,7 +296,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc55842d0db6329a669d55a623c674b02d677b16bfb2d24857d4089d41eba882" dependencies = [ "gimli", - "libc", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4a76eae4..365b0f3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,10 +51,10 @@ unwind = ["unwinding"] yyjson = [] # Features detected by build.rs. Do not specify. +inline_int = [] intrinsics = [] optimize = [] -strict_provenance = [] -inline_int = [] +setitem_knownhash = [] [dependencies] associative-cache = { version = "2", default-features = false } @@ -72,7 +72,7 @@ serde = { version = "1", default-features = false } serde_json = { version = "1", default-features = false, features = ["std", "float_roundtrip"] } simdutf8 = { version = "0.1", default-features = false, features = ["std", "public_imp", "aarch64_neon"] } smallvec = { version = "^1.11", default-features = false, features = ["union", "write"] } -unwinding = { version = "=0.2.2", features = ["unwinder"], optional = true } +unwinding = { version = "=0.2.2", default-features = false, features = ["unwinder"], optional = true } uuid = { version = "1", default-features = false } xxhash-rust = { version = "^0.8", default-features = false, features = ["xxh3"] } diff --git a/README.md b/README.md index 2fa365c7..942597ef 100644 --- a/README.md +++ b/README.md @@ -1202,6 +1202,14 @@ level above this. No. `bytes` is the correct type for a serialized blob. +### Will it support NDJSON or JSONL? + +No. [orjsonl](https://github.com/umarbutler/orjsonl) may be appropriate. + +### Will it support JSON5 or RJSON? + +No, it supports RFC 8259. + ## Packaging To package orjson requires at least [Rust](https://www.rust-lang.org/) 1.72 diff --git a/build.rs b/build.rs index 4eac1746..3dfd2cc5 100644 --- a/build.rs +++ b/build.rs @@ -23,10 +23,15 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(Py_3_9)"); println!("cargo:rustc-check-cfg=cfg(Py_GIL_DISABLED)"); - for cfg in pyo3_build_config::get().build_script_outputs() { + let python_config = pyo3_build_config::get(); + for cfg in python_config.build_script_outputs() { println!("{cfg}"); } + if python_config.version.minor != 13 { + println!("cargo:rustc-cfg=feature=\"setitem_knownhash\""); + } + if let Some(true) = version_check::supports_feature("core_intrinsics") { println!("cargo:rustc-cfg=feature=\"intrinsics\""); } @@ -35,10 +40,6 @@ fn main() { println!("cargo:rustc-cfg=feature=\"optimize\""); } - if let Some(true) = version_check::supports_feature("strict_provenance") { - println!("cargo:rustc-cfg=feature=\"strict_provenance\""); - } - #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] if env::var("ORJSON_DISABLE_SIMD").is_err() { // auto build unstable SIMD on nightly diff --git a/ci/azure-win.yml b/ci/azure-win.yml index 0424b536..655eab96 100644 --- a/ci/azure-win.yml +++ b/ci/azure-win.yml @@ -24,7 +24,7 @@ steps: env: CFLAGS: "-Os -flto" LDFLAGS: "-Wl,--as-needed" - RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -Z virtual-function-elimination -D warnings" + RUSTFLAGS: "-C lto=fat -Z mir-opt-level=4 -D warnings" CARGO_UNSTABLE_SPARSE_REGISTRY: "true" UNSAFE_PYO3_SKIP_VERSION_CHECK: "1" - script: python.exe -m pip install orjson --no-index --find-links=D:\a\1\s\target\wheels diff --git a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs index 97b2f5e0..884d9b92 100644 --- a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs +++ b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs @@ -9,6 +9,17 @@ pub struct PyCriticalSection { _cs_mutex: *mut PyMutex, } +#[cfg(Py_GIL_DISABLED)] +impl Default for PyCriticalSection { + fn default() -> Self { + let mut mutex = PyMutex::new(); + PyCriticalSection { + _cs_prev: 0, + _cs_mutex: &mut mutex, + } + } +} + #[repr(C)] #[cfg(Py_GIL_DISABLED)] pub struct PyCriticalSection2 { diff --git a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs index 79dcbfdb..0811326e 100644 --- a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs +++ b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs @@ -36,6 +36,15 @@ extern "C" { item: *mut PyObject, hash: crate::Py_hash_t, ) -> c_int; + + #[cfg(Py_3_14)] + pub fn _PyDict_SetItem_KnownHash_LockHeld( + mp: *mut PyDictObject, + name: *mut PyObject, + value: *mut PyObject, + hash: crate::Py_hash_t, + ) -> c_int; + // skipped _PyDict_DelItem_KnownHash // skipped _PyDict_DelItemIf // skipped _PyDict_NewKeysForClass diff --git a/include/pyo3/pyo3-ffi/src/datetime.rs b/include/pyo3/pyo3-ffi/src/datetime.rs index 7283b6d4..76d12151 100644 --- a/include/pyo3/pyo3-ffi/src/datetime.rs +++ b/include/pyo3/pyo3-ffi/src/datetime.rs @@ -4,16 +4,17 @@ //! and covers the various date and time related objects in the Python `datetime` //! standard library module. +#[cfg(not(PyPy))] +use crate::PyCapsule_Import; #[cfg(GraalPy)] use crate::{PyLong_AsLong, PyLong_Check, PyObject_GetAttrString, Py_DecRef}; use crate::{PyObject, PyObject_TypeCheck, PyTypeObject, Py_TYPE}; -use std::cell::UnsafeCell; #[cfg(not(GraalPy))] use std::os::raw::c_char; use std::os::raw::c_int; use std::ptr; -#[cfg(not(PyPy))] -use {crate::PyCapsule_Import, std::ffi::CString}; +use std::sync::Once; +use std::{cell::UnsafeCell, ffi::CStr}; #[cfg(not(any(PyPy, GraalPy)))] use {crate::Py_hash_t, std::os::raw::c_uchar}; // Type struct wrappers @@ -593,6 +594,8 @@ pub struct PyDateTime_CAPI { // Python already shares this object between threads, so it's no more evil for us to do it too! unsafe impl Sync for PyDateTime_CAPI {} +pub const PyDateTime_CAPSULE_NAME: &CStr = c_str!("datetime.datetime_CAPI"); + /// Returns a pointer to a `PyDateTime_CAPI` instance /// /// # Note @@ -600,33 +603,38 @@ unsafe impl Sync for PyDateTime_CAPI {} /// `PyDateTime_IMPORT` is called #[inline] pub unsafe fn PyDateTimeAPI() -> *mut PyDateTime_CAPI { - *PyDateTimeAPI_impl.0.get() -} - -#[inline] -pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject { - (*PyDateTimeAPI()).TimeZone_UTC + *PyDateTimeAPI_impl.ptr.get() } /// Populates the `PyDateTimeAPI` object pub unsafe fn PyDateTime_IMPORT() { - // PyPy expects the C-API to be initialized via PyDateTime_Import, so trying to use - // `PyCapsule_Import` will behave unexpectedly in pypy. - #[cfg(PyPy)] - let py_datetime_c_api = PyDateTime_Import(); - - #[cfg(not(PyPy))] - let py_datetime_c_api = { - // PyDateTime_CAPSULE_NAME is a macro in C - let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); - - PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI - }; + if !PyDateTimeAPI_impl.once.is_completed() { + // PyPy expects the C-API to be initialized via PyDateTime_Import, so trying to use + // `PyCapsule_Import` will behave unexpectedly in pypy. + #[cfg(PyPy)] + let py_datetime_c_api = PyDateTime_Import(); + + #[cfg(not(PyPy))] + let py_datetime_c_api = + PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI; + + if py_datetime_c_api.is_null() { + return; + } - *PyDateTimeAPI_impl.0.get() = py_datetime_c_api; + // Protect against race conditions when the datetime API is concurrently + // initialized in multiple threads. UnsafeCell.get() cannot panic so this + // won't panic either. + PyDateTimeAPI_impl.once.call_once(|| { + *PyDateTimeAPI_impl.ptr.get() = py_datetime_c_api; + }); + } } -// skipped non-limited PyDateTime_TimeZone_UTC +#[inline] +pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject { + (*PyDateTimeAPI()).TimeZone_UTC +} /// Type Check macros /// @@ -739,8 +747,13 @@ extern "C" { // Rust specific implementation details -struct PyDateTimeAPISingleton(UnsafeCell<*mut PyDateTime_CAPI>); +struct PyDateTimeAPISingleton { + once: Once, + ptr: UnsafeCell<*mut PyDateTime_CAPI>, +} unsafe impl Sync for PyDateTimeAPISingleton {} -static PyDateTimeAPI_impl: PyDateTimeAPISingleton = - PyDateTimeAPISingleton(UnsafeCell::new(ptr::null_mut())); +static PyDateTimeAPI_impl: PyDateTimeAPISingleton = PyDateTimeAPISingleton { + once: Once::new(), + ptr: UnsafeCell::new(ptr::null_mut()), +}; diff --git a/include/pyo3/pyo3-ffi/src/lib.rs b/include/pyo3/pyo3-ffi/src/lib.rs index c6157401..293c5171 100644 --- a/include/pyo3/pyo3-ffi/src/lib.rs +++ b/include/pyo3/pyo3-ffi/src/lib.rs @@ -43,10 +43,39 @@ //! PyO3 uses `rustc`'s `--cfg` flags to enable or disable code used for different Python versions. //! If you want to do this for your own crate, you can do so with the [`pyo3-build-config`] crate. //! -//! - `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when -//! compiling for a given minimum Python version. +//! - `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`, `Py_3_11`, `Py_3_12`, `Py_3_13`: Marks code that is +//! only enabled when compiling for a given minimum Python version. //! - `Py_LIMITED_API`: Marks code enabled when the `abi3` feature flag is enabled. +//! - `Py_GIL_DISABLED`: Marks code that runs only in the free-threaded build of CPython. //! - `PyPy` - Marks code enabled when compiling for PyPy. +//! - `GraalPy` - Marks code enabled when compiling for GraalPy. +//! +//! Additionally, you can query for the values `Py_DEBUG`, `Py_REF_DEBUG`, +//! `Py_TRACE_REFS`, and `COUNT_ALLOCS` from `py_sys_config` to query for the +//! corresponding C build-time defines. For example, to conditionally define +//! debug code using `Py_DEBUG`, you could do: +//! +//! ```rust,ignore +//! #[cfg(py_sys_config = "Py_DEBUG")] +//! println!("only runs if python was compiled with Py_DEBUG") +//! ``` +//! +//! To use these attributes, add [`pyo3-build-config`] as a build dependency in +//! your `Cargo.toml`: +//! +//! ```toml +//! [build-dependency] +//! pyo3-build-config = "VER" +//! ``` +//! +//! And then either create a new `build.rs` file in the project root or modify +//! the existing `build.rs` file to call `use_pyo3_cfgs()`: +//! +//! ```rust,ignore +//! fn main() { +//! pyo3_build_config::use_pyo3_cfgs(); +//! } +//! ``` //! //! # Minimum supported Rust and Python versions //! diff --git a/pyproject.toml b/pyproject.toml index 38ae9c19..f72f842c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ ] [project.urls] +Documentation = "https://github.com/ijl/orjson" Changelog = "https://github.com/ijl/orjson/blob/master/CHANGELOG.md" [build-system] @@ -58,3 +59,7 @@ known-first-party = ["orjson"] [tool.mypy] python_version = "3.8" + +[[tool.mypy.overrides]] +module = ["dateutil", "pytz", "simplejson", "ujson"] +ignore_missing_imports = true diff --git a/requirements.txt b/requirements.txt index 27539a21..9981cc8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,9 +2,5 @@ -r integration/requirements.txt -r test/requirements.txt maturin -mypy==1.12.0 -ruff==0.7.0 -types-python-dateutil -types-pytz -types-simplejson -types-ujson +mypy==1.13.0 +ruff==0.7.1 diff --git a/script/develop b/script/develop index fcab003b..b92a22e9 100755 --- a/script/develop +++ b/script/develop @@ -14,7 +14,9 @@ echo "CC: ${CC}, LD: ${LD}, LD_LIBRARY_PATH: ${LD_LIBRARY_PATH}" export CFLAGS="-Os -fstrict-aliasing -fno-plt -flto=full -emit-llvm" export LDFLAGS="-fuse-ld=${LD} -Wl,-plugin-opt=also-emit-llvm -Wl,--as-needed -Wl,-zrelro,-znow" -export RUSTFLAGS="-C linker=${CC} -C link-arg=-fuse-ld=${LD} -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z virtual-function-elimination -Z threads=8" +export RUSTFLAGS="-C linker=${CC} -C link-arg=-fuse-ld=${LD} -C linker-plugin-lto -C lto=fat -C link-arg=-Wl,-zrelro,-znow -Z mir-opt-level=4 -Z threads=8" + +rm -f ${CARGO_TARGET_DIR}/wheels/*.whl maturin build --target="${TARGET}" "$@" diff --git a/script/pytest b/script/pytest index 6b95014c..e8e7e51d 100755 --- a/script/pytest +++ b/script/pytest @@ -1,3 +1,3 @@ #!/bin/sh -e -PYTHONMALLOC="debug" pytest -s -rxX --random-order -n 4 test +PYTHONMALLOC="debug" pytest -s test diff --git a/src/lib.rs b/src/lib.rs index 5fe295eb..64ace9e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,6 @@ #![cfg_attr(feature = "avx512", feature(stdarch_x86_avx512, avx512_target_feature))] #![cfg_attr(feature = "intrinsics", feature(core_intrinsics))] #![cfg_attr(feature = "optimize", feature(optimize_attribute))] -#![cfg_attr(feature = "strict_provenance", feature(strict_provenance))] -#![cfg_attr(feature = "strict_provenance", warn(fuzzy_provenance_casts))] #![cfg_attr(feature = "unstable-simd", feature(portable_simd))] #![allow(internal_features)] // core_intrinsics #![allow(non_camel_case_types)] diff --git a/test/requirements.txt b/test/requirements.txt index b07f7a6b..2c04102f 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -3,10 +3,8 @@ faker numpy;(platform_machine=="x86_64" or (platform_machine=="aarch64" and sys_platform == "linux")) and python_version<"3.13" pendulum;sys_platform=="linux" and platform_machine=="x86_64" and python_version<"3.12" time-machine < 2.15;sys_platform=="linux" and platform_machine=="x86_64" and python_version<"3.12" -psutil;(sys_platform=="linux" or sys_platform == "macos") and platform_machine=="x86_64" +psutil;(sys_platform=="linux" or sys_platform == "macos") and platform_machine=="x86_64" and python_version<"3.13" pytest -pytest-random-order -pytest-xdist pytz typing_extensions;python_version<"3.8" xxhash==1.4.3;sys_platform=="linux" and platform_machine=="x86_64" and python_version<"3.9" # creates non-compact ASCII for test_str_ascii