diff --git a/.github/workflows/build-sdist.yml b/.github/workflows/build-sdist.yml new file mode 100644 index 0000000..e153b21 --- /dev/null +++ b/.github/workflows/build-sdist.yml @@ -0,0 +1,61 @@ +name: Build sdist + +on: + workflow_call: + inputs: + dev: + default: false + required: false + type: boolean + workflow_dispatch: + inputs: + dev: + default: false + required: false + type: boolean + +concurrency: + group: build-sdist-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + build_sdist: + name: Build sdist + runs-on: ubuntu-latest + env: + UV_NO_PROGRESS: true + UV_SYSTEM_PYTHON: true + permissions: + contents: read + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: marcelotduarte/python-msilib + persist-credentials: false + + - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + with: + enable-cache: false + python-version: "3.14" + + - name: Bump new dev version + if: ${{ inputs.dev }} + run: ./ci/bump-version.sh build-dev + + - name: Build source distribution + run: uv build --sdist + + - name: Upload the artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: python-msilib-sdist + path: dist + compression-level: 0 # no compression diff --git a/.github/workflows/build.yml b/.github/workflows/build-wheel.yml similarity index 81% rename from .github/workflows/build.yml rename to .github/workflows/build-wheel.yml index 565796a..241f2c7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build-wheel.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: concurrency: - group: build-${{ github.ref }} + group: build-wheel-${{ github.ref }} cancel-in-progress: false permissions: @@ -19,11 +19,6 @@ jobs: name: Generate matrix of builds runs-on: ubuntu-slim steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 - with: - egress-policy: audit - - name: Extra includes id: includes run: | @@ -56,8 +51,16 @@ jobs: outputs: data: ${{ steps.includes.outputs.data }} + build_sdist: + name: Build sdist for python-msilib + uses: $/.github/workflows/build-sdist.yml + with: + dev: ${{ github.event_name != 'release' }} + build_wheel: - needs: matrix + needs: + - build_sdist + - matrix strategy: fail-fast: false matrix: ${{ fromJSON(needs.matrix.outputs.data) }} @@ -76,10 +79,18 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Download sdist artifact + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: - repository: marcelotduarte/python-msilib - persist-credentials: false + name: python-msilib-sdist + path: dist + + - name: Checkout from sdist + run: | + (cd dist && tar xvf python_msilib-*.tar.gz) + mv dist/python_msilib-*/* . + rm -rf dist + ls -l - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -92,14 +103,9 @@ jobs: cache-dependency-glob: "pyproject.toml" enable-cache: false - - name: Bump new dev version - if: github.event_name != 'release' - run: ./ci/bump-version.sh build-dev - - - name: Build sdist and wheels + - name: Build wheels run: ./ci/build-wheel.sh "${MATRIX_TAG}" env: - BUILD_SDIST: true MATRIX_TAG: ${{ matrix.tag }} - name: Upload the artifact @@ -112,7 +118,8 @@ jobs: publish: if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags') name: Publish package to PyPI - needs: build_wheel + needs: + - build_wheel environment: name: pypi url: https://pypi.org/p/python-msilib @@ -126,10 +133,10 @@ jobs: egress-policy: audit - name: Download all artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: path: wheelhouse - pattern: python-msilib-wheel-* + pattern: python-msilib-* merge-multiple: true - name: Publish package to PyPI diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6676fd..cd75760 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,13 +43,16 @@ jobs: type_check: name: Type check + if: | + contains(github.event.pull_request.title, '[skip tests]') == false && + contains(github.event.head_commit.message, '[skip tests]') == false runs-on: ${{ matrix.os }}-${{ matrix.os_version }} strategy: fail-fast: false matrix: os: [windows] os_version: [latest] - python-version: ["3.13", "3.14", "3.15-dev"] + python-version: ["3.13", "3.14", "3.15"] defaults: run: shell: bash @@ -70,6 +73,7 @@ jobs: activate-environment: true cache-dependency-glob: "pyproject.toml" enable-cache: true + python-version: ${{ matrix.python-version }} - name: Install the dependencies for the tests run: uv pip install --upgrade -r pyproject.toml --group tests @@ -81,7 +85,7 @@ jobs: build_wheel: name: Build wheels - uses: $/.github/workflows/build.yml + uses: $/.github/workflows/build-wheel.yml needs: - pre_commit - type_check @@ -131,7 +135,7 @@ jobs: enable-cache: true - name: Download the wheel - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: merge-multiple: true pattern: python-msilib-wheel-${{ matrix.os }}* @@ -174,7 +178,7 @@ jobs: persist-credentials: true - name: Download coverage data - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: pattern: cov-* merge-multiple: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e440121..c2ca9d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,10 +23,10 @@ repos: hooks: - id: validate-pyproject additional_dependencies: - - validate-pyproject-schema-store[all]==2026.09.30 + - validate-pyproject-schema-store[all]==2026.10.06 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9 + rev: 44b5042edab9382aba712eed4f9c7bab7a3d6a3e # frozen: v0.17.0 hooks: - id: ruff-check args: ["--fix", "--show-fixes", "--unsafe-fixes"] @@ -39,9 +39,10 @@ repos: additional_dependencies: - tomli args: ["--ignore-words-list=lief,LIEF", "-w", "--skip=*.rtf"] + exclude: (?x)^(src/msilib/include/pythoncapi_compat.h)$ - repo: https://github.com/pre-commit/mirrors-clang-format - rev: e2b496dc2bd8340c2524cb9a2d2a943cde1bb6df # frozen: v23.1.1 + rev: 98ad4ddb9c057de2f5b9e93e44e6db959807d68c # frozen: v23.1.3 hooks: - id: clang-format exclude: (?x)^(src/msilib/include/pythoncapi_compat.h)$ @@ -59,7 +60,7 @@ repos: - id: shellcheck - repo: https://github.com/astral-sh/ty-pre-commit - rev: 6972e4c5a7e9f45f05a2f30b668f221f43b770c9 # frozen: v0.0.84 + rev: ded8634232ffc633dc66d44f1b0dfe2bdf766542 # frozen: v0.0.86 hooks: - id: ty args: [--no-sync] diff --git a/MANIFEST.in b/MANIFEST.in index de487bd..a9b9c89 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include Makefile recursive-include src *.c recursive-include src *.h -recursive-include tests *.py +graft ci +graft tests diff --git a/ci/build-wheel.sh b/ci/build-wheel.sh index dc24bec..e5c1268 100755 --- a/ci/build-wheel.sh +++ b/ci/build-wheel.sh @@ -35,7 +35,11 @@ if [ "$IS_CONDA" == "1" ]; then PLATFORM_TAG_MASK=$PLATFORM_TAG else if [[ $PY_PLATFORM == linux* ]]; then - PLATFORM_TAG=many${PY_PLATFORM/-/_} + if [ "$OSTYPE" == "linux-musl" ]; then + PLATFORM_TAG=musl${PY_PLATFORM/-/_} + else + PLATFORM_TAG=many${PY_PLATFORM/-/_} + fi PLATFORM_TAG_MASK=${PLATFORM_TAG/_/*_} elif [[ $PY_PLATFORM == macosx* ]]; then PLATFORM_TAG=macosx_universal2 @@ -134,7 +138,8 @@ _build_wheel () { if [ "$CI" == "true" ] && [[ $PY_PLATFORM == win* ]]; then export UV_LINK_MODE=copy fi - if [ "$ZIP_SAFE" == "true" ]; then + # check for $container while cibuildwheel does not support to run on it + if [ "$ZIP_SAFE" == "true" ] || [ -n "$container" ]; then UV_NO_BUILD=0 \ uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse else diff --git a/pyproject.toml b/pyproject.toml index eeb9616..bda43b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,8 +47,8 @@ Documentation = "https://docs.python.org/3.12/library/msilib.html" [dependency-groups] dev = [ - "cibuildwheel>=4.2.1", - "prek>=0.5.4,<0.6.0", + "cibuildwheel>=4.3.0", + "prek>=0.5.5,<0.6.0", ] tests = [ "coverage>=7.13.0", diff --git a/requirements-dev.txt b/requirements-dev.txt index a682444..b846381 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,2 @@ -cibuildwheel>=4.2.1 -prek>=0.5.4,<0.6.0 +cibuildwheel>=4.3.0 +prek>=0.5.5,<0.6.0 diff --git a/src/msilib/include/pythoncapi_compat.h b/src/msilib/include/pythoncapi_compat.h index fa47980..8ad5cc7 100644 --- a/src/msilib/include/pythoncapi_compat.h +++ b/src/msilib/include/pythoncapi_compat.h @@ -21,44 +21,109 @@ extern "C" { #include #include // offsetof() +#include // fclose() +#include // memcpy() -// Python 3.11.0b4 added PyFrame_Back() to Python.h -#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) -# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() +// Python 3.11 added PyFrame_GetBack() to Python.h +#if PY_VERSION_HEX < 0x030B0000 && !defined(PYPY_VERSION) +# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() #endif #ifndef _Py_CAST -# define _Py_CAST(type, expr) ((type)(expr)) +# define _Py_CAST(type, expr) ((type)(expr)) #endif // Static inline functions should use _Py_NULL rather than using directly NULL // to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, // _Py_NULL is defined as nullptr. #ifndef _Py_NULL -# if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ - || (defined(__cplusplus) && __cplusplus >= 201103) -# define _Py_NULL nullptr -# else -# define _Py_NULL NULL -# endif +# if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ + || (defined(__cplusplus) && __cplusplus >= 201103) +# define _Py_NULL nullptr +# else +# define _Py_NULL NULL +# endif #endif // Cast argument to PyObject* type. #ifndef _PyObject_CAST -# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +#endif +#ifndef _PyVarObject_CAST +# define _PyVarObject_CAST(op) _Py_CAST(PyVarObject*, (op)) #endif #ifndef Py_BUILD_ASSERT -# define Py_BUILD_ASSERT(cond) \ +# define Py_BUILD_ASSERT(cond) \ + do { (void)sizeof(char [1 - 2 * !(cond)]); } while(0) +#endif + +#ifndef _Py_COMP_DIAG_PUSH +# if defined(__clang__) +# define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") +# define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +# define _Py_COMP_DIAG_POP _Pragma("clang diagnostic pop") +# elif defined(__GNUC__) \ + && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) +# define _Py_COMP_DIAG_PUSH _Pragma("GCC diagnostic push") +# define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define _Py_COMP_DIAG_POP _Pragma("GCC diagnostic pop") +# elif defined(_MSC_VER) +# define _Py_COMP_DIAG_PUSH __pragma(warning(push)) +# define _Py_COMP_DIAG_IGNORE_DEPR_DECLS __pragma(warning(disable: 4996)) +# define _Py_COMP_DIAG_POP __pragma(warning(pop)) +# else +# define _Py_COMP_DIAG_PUSH +# define _Py_COMP_DIAG_IGNORE_DEPR_DECLS +# define _Py_COMP_DIAG_POP +# endif +#endif + +#ifndef _Py_PACK_VERSION +# define _Py_PACK_FULL_VERSION(X, Y, Z, LEVEL, SERIAL) ( \ + (((X) & 0xff) << 24) | \ + (((Y) & 0xff) << 16) | \ + (((Z) & 0xff) << 8) | \ + (((LEVEL) & 0xf) << 4) | \ + (((SERIAL) & 0xf) << 0)) +# define _Py_PACK_VERSION(X, Y) _Py_PACK_FULL_VERSION(X, Y, 0, 0, 0) +#endif + +#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 13) \ + && (!defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 13))) + // "%T" format was added to Python 3.13 +# define PYTHONCAPI_COMPAT_EXPECT_TYPE(ERRMSG, OBJ) \ + PyErr_Format(PyExc_TypeError, ERRMSG "%T", (OBJ)) +#elif !defined(Py_LIMITED_API) +# define PYTHONCAPI_COMPAT_EXPECT_TYPE(ERRMSG, OBJ) \ + PyErr_Format(PyExc_TypeError, ERRMSG "%s", Py_TYPE(OBJ)->tp_name) +#elif Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 11) +# define PYTHONCAPI_COMPAT_EXPECT_TYPE(ERRMSG, OBJ) \ + do { \ + PyObject *_type_name = PyType_GetName(Py_TYPE(OBJ)); \ + if (_type_name != NULL) { \ + PyErr_Format(PyExc_TypeError, ERRMSG "%S", (OBJ)); \ + Py_DECREF(_type_name); \ + } \ + } while (0) +#else +# define PYTHONCAPI_COMPAT_EXPECT_TYPE(ERRMSG, OBJ) \ do { \ - (void)sizeof(char [1 - 2 * !(cond)]); \ - } while(0) + PyObject *_type = _PyObject_CAST(Py_TYPE(OBJ)); \ + PyObject *_type_name = PyObject_GetAttrString(_type, "__name__"); \ + if (_type_name != NULL) { \ + PyErr_Format(PyExc_TypeError, ERRMSG "%S", (OBJ)); \ + Py_DECREF(_type_name); \ + } \ + } while (0) #endif -// bpo-42262 added Py_NewRef() to Python 3.10.0a3 -#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) +// bpo-42262 added Py_NewRef() to Python 3.10 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 10) && !defined(Py_NewRef) static inline PyObject* _Py_NewRef(PyObject *obj) { Py_INCREF(obj); @@ -68,8 +133,8 @@ static inline PyObject* _Py_NewRef(PyObject *obj) #endif -// bpo-42262 added Py_XNewRef() to Python 3.10.0a3 -#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) +// bpo-42262 added Py_XNewRef() to Python 3.10 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 10) && !defined(Py_XNewRef) static inline PyObject* _Py_XNewRef(PyObject *obj) { Py_XINCREF(obj); @@ -79,8 +144,8 @@ static inline PyObject* _Py_XNewRef(PyObject *obj) #endif -// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 -#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) +// bpo-39573 added Py_SET_REFCNT() to Python 3.9 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) && !defined(Py_SET_REFCNT) static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { ob->ob_refcnt = refcnt; @@ -90,23 +155,22 @@ static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) // bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() -// to Python 3.10.0b1. -#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) -# define Py_Is(x, y) ((x) == (y)) -#endif -#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) -# define Py_IsNone(x) Py_Is(x, Py_None) +// to Python 3.10. +// PyPy 3.11 provides Py_Is() and Py_IsNone(). +// PyPy 3.12 provides Py_IsTrue() and Py_IsFalse(). +#if !defined(Py_Is) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 10) +# define Py_Is(x, y) ((x) == (y)) +# define Py_IsNone(x) Py_Is(x, Py_None) #endif -#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) -# define Py_IsTrue(x) Py_Is(x, Py_True) -#endif -#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) -# define Py_IsFalse(x) Py_Is(x, Py_False) +#if (!defined(Py_IsTrue) \ + && (PY_VERSION_HEX < _Py_PACK_VERSION(3, 10) || defined(PYPY_VERSION))) +# define Py_IsTrue(x) Py_Is(x, Py_True) +# define Py_IsFalse(x) Py_Is(x, Py_False) #endif -// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 -#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +// bpo-39573 added Py_SET_TYPE() to Python 3.9 +#if !defined(Py_SET_TYPE) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { ob->ob_type = type; @@ -115,8 +179,8 @@ static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) #endif -// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 -#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) +// bpo-39573 added Py_SET_SIZE() to Python 3.9 +#if !defined(Py_SET_SIZE) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { ob->ob_size = size; @@ -125,9 +189,11 @@ static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) #endif -// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 +// bpo-40421 added PyFrame_GetCode() to Python 3.9 // PyPy added PyFrame_GetCode() to PyPy3.12 v8.0.0 -#if PY_VERSION_HEX < 0x030900B1 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030C0000) +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 12))) \ + && !defined(Py_LIMITED_API) static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) { assert(frame != _Py_NULL); @@ -136,16 +202,19 @@ static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) } #endif +#if !defined(Py_LIMITED_API) || PY_VERSION_HEX >= _Py_PACK_VERSION(3, 9) static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) { PyCodeObject *code = PyFrame_GetCode(frame); Py_DECREF(code); return code; } +#endif -// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 -#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +// bpo-40421 added PyFrame_GetBack() to Python 3.9 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) { assert(frame != _Py_NULL); @@ -153,7 +222,7 @@ static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) } #endif -#if !defined(PYPY_VERSION) +#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) { PyFrameObject *back = PyFrame_GetBack(frame); @@ -163,8 +232,9 @@ static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) #endif -// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 -#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +// bpo-40421 added PyFrame_GetLocals() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) { if (PyFrame_FastToLocalsWithError(frame) < 0) { @@ -175,8 +245,9 @@ static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) #endif -// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 -#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +// bpo-40421 added PyFrame_GetGlobals() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) { return Py_NewRef(frame->f_globals); @@ -184,8 +255,9 @@ static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) #endif -// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 -#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) { return Py_NewRef(frame->f_builtins); @@ -193,12 +265,13 @@ static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) #endif -// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 -#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +// bpo-40421 added PyFrame_GetLasti() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline int PyFrame_GetLasti(PyFrameObject *frame) { -#if PY_VERSION_HEX >= 0x030A00A7 - // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 10) + // bpo-27129: Since Python 3.10, f_lasti is an instruction offset, // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) // instructions. if (frame->f_lasti < 0) { @@ -212,8 +285,9 @@ static inline int PyFrame_GetLasti(PyFrameObject *frame) #endif -// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 -#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +// gh-91248 added PyFrame_GetVar() to Python 3.12 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 12) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) { PyObject *locals, *value; @@ -237,8 +311,9 @@ static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) #endif -// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 -#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +// gh-91248 added PyFrame_GetVarString() to Python 3.12 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 12) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyFrame_GetVarString(PyFrameObject *frame, const char *name) { @@ -254,8 +329,11 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name) #endif -// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) +// Added to Python 3.9 and limited C API 3.9. +// Added to PyPy 3.11. +#if ((PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 11))) \ + && !defined(Py_LIMITED_API)) static inline PyInterpreterState * PyThreadState_GetInterpreter(PyThreadState *tstate) { @@ -265,8 +343,9 @@ PyThreadState_GetInterpreter(PyThreadState *tstate) #endif -// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 -#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +// bpo-40429 added PyThreadState_GetFrame() to Python 3.9 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != _Py_NULL); @@ -274,20 +353,24 @@ static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) } #endif -#if !defined(PYPY_VERSION) +#if ((!defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 9)) \ + && (!defined(PYPY_VERSION) || PY_VERSION_HEX >= _Py_PACK_VERSION(3, 11))) static inline PyFrameObject* _PyThreadState_GetFrameBorrow(PyThreadState *tstate) { PyFrameObject *frame = PyThreadState_GetFrame(tstate); - Py_XDECREF(frame); + Py_XDECREF(_PyObject_CAST(frame)); return frame; } #endif -// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 -// PyPy added PyInterpreterState_Get() to PyPy3.12 v8.0.0 -#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030C0000) +// Added to Python 3.9 and limited C API 3.9. On Windows, it's not available +// before stable ABI 3.10. +// Added to PyPy3.12 v8.0.0. +#if ((PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 12))) \ + && !defined(Py_LIMITED_API)) static inline PyInterpreterState* PyInterpreterState_Get(void) { PyThreadState *tstate; @@ -306,8 +389,9 @@ static inline PyInterpreterState* PyInterpreterState_Get(void) #endif -// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 -#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +// bpo-39947 added PyThreadState_GetID() to Python 3.9 +#if (_Py_PACK_VERSION(3, 7) <= PY_VERSION_HEX && PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) { assert(tstate != _Py_NULL); @@ -315,12 +399,13 @@ static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) } #endif -// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 -#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline void PyThreadState_EnterTracing(PyThreadState *tstate) { tstate->tracing++; -#if PY_VERSION_HEX >= 0x030A00A1 +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 10) tstate->cframe->use_tracing = 0; #else tstate->use_tracing = 0; @@ -328,14 +413,15 @@ static inline void PyThreadState_EnterTracing(PyThreadState *tstate) } #endif -// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 -#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) { int use_tracing = (tstate->c_tracefunc != _Py_NULL || tstate->c_profilefunc != _Py_NULL); tstate->tracing--; -#if PY_VERSION_HEX >= 0x030A00A1 +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 10) tstate->cframe->use_tracing = use_tracing; #else tstate->use_tracing = use_tracing; @@ -344,9 +430,11 @@ static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) #endif -// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 +// bpo-37194 added PyObject_CallNoArgs() to Python 3.9 // PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 -#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +#if (!defined(PyObject_CallNoArgs) \ + && (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 9)))) static inline PyObject* PyObject_CallNoArgs(PyObject *func) { return PyObject_CallFunctionObjArgs(func, NULL); @@ -355,9 +443,10 @@ static inline PyObject* PyObject_CallNoArgs(PyObject *func) // bpo-39245 made PyObject_CallOneArg() public (previously called -// _PyObject_CallOneArg) in Python 3.9.0a4 +// _PyObject_CallOneArg) in Python 3.9 // PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 -#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +#if (!defined(PyObject_CallOneArg) \ + && (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) || defined(Py_LIMITED_API))) static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) { return PyObject_CallFunctionObjArgs(func, arg, NULL); @@ -365,8 +454,11 @@ static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) #endif -// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 -#if PY_VERSION_HEX < 0x030A00A3 +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10. +// The function was aded to the limited C API 3.10. +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 10) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10)) \ + && !(_Py_PACK_VERSION(3, 10) <= PY_VERSION_HEX && PY_VERSION_HEX < _Py_PACK_VERSION(3, 11))) static inline int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) { @@ -390,16 +482,26 @@ PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) #endif -// bpo-40024 added PyModule_AddType() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 -static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +// bpo-40024 added PyModule_AddType() to Python 3.9 +// On Windows, PyModule_AddType() was added to limited C API 3.9. +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 9)) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) && defined(MS_WINDOWS))) +static inline int +_PythonCAPICompat_Module_AddType(PyObject *module, PyTypeObject *type) { +#ifndef Py_LIMITED_API const char *name, *dot; +#else + PyObject *name; + int res; +#endif if (PyType_Ready(type) < 0) { return -1; } +#ifndef Py_LIMITED_API // inline _PyType_Name() name = type->tp_name; assert(name != _Py_NULL); @@ -409,22 +511,55 @@ static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) } return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); +#else + if (!PyModule_Check(module)) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObjectRef() first argument " + "must be a module"); + return -1; + } + + name = PyObject_GetAttrString(_PyObject_CAST(type), "__name__"); + if (name == NULL) { + return -1; + } + + PyObject *dict = PyModule_GetDict(module); // borrowed ref + res = PyDict_SetItem(dict, name, _PyObject_CAST(type)); + Py_DECREF(name); + return res; +#endif +} + +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) && defined(MS_WINDOWS) + // On Python 3.9, PyModule_AddType() is part of the limited C API, + // but the function is not exported in the stable ABI. +# define PyModule_AddType _PythonCAPICompat_Module_AddType +#else +static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +{ + return _PythonCAPICompat_Module_AddType(module, type); } #endif +#endif -// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. +// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9. // bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. -#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +// On Windows, PyObject_GC_IsTracked() was only added to stable ABI 3.10. +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline int PyObject_GC_IsTracked(PyObject* obj) { return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); } #endif -// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. +// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9. // bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. -#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +// On Windows, PyObject_GC_IsFinalized() was only added to stable ABI 3.10. +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline int PyObject_GC_IsFinalized(PyObject *obj) { PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; @@ -433,8 +568,8 @@ static inline int PyObject_GC_IsFinalized(PyObject *obj) #endif -// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 -#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) +// bpo-39573 added Py_IS_TYPE() to Python 3.9 +#if !defined(Py_IS_TYPE) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { return Py_TYPE(ob) == type; } @@ -443,10 +578,11 @@ static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { // bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. -// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6. // Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal // C API: Python 3.11a2-3.11a6 versions are not supported. -#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX <= _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline int PyFloat_Pack2(double x, char *p, int le) { return _PyFloat_Pack2(x, (unsigned char*)p, le); } @@ -460,7 +596,8 @@ static inline double PyFloat_Unpack2(const char *p, int le) // Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() // and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions // are not supported. -#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX <= _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline int PyFloat_Pack4(double x, char *p, int le) { return _PyFloat_Pack4(x, (unsigned char*)p, le); } @@ -475,8 +612,9 @@ static inline double PyFloat_Unpack8(const char *p, int le) #endif -// gh-92154 added PyCode_GetCode() to Python 3.11.0b1 -#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +// gh-92154 added PyCode_GetCode() to Python 3.11 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyCode_GetCode(PyCodeObject *code) { return Py_NewRef(code->co_code); @@ -485,7 +623,8 @@ static inline PyObject* PyCode_GetCode(PyCodeObject *code) // gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 -#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) { return Py_NewRef(code->co_varnames); @@ -493,7 +632,8 @@ static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) #endif // gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 -#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) { return Py_NewRef(code->co_freevars); @@ -501,7 +641,8 @@ static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) #endif // gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 -#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) { return Py_NewRef(code->co_cellvars); @@ -509,18 +650,19 @@ static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) #endif -// Py_UNUSED() was added to Python 3.4.0b2. +// Py_UNUSED() was added to Python 3.4. #ifndef Py_UNUSED -# if defined(__GNUC__) || defined(__clang__) -# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) -# else -# define Py_UNUSED(name) _unused_ ## name -# endif +# if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +# else +# define Py_UNUSED(name) _unused_ ## name +# endif #endif -// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A0 +// PyImport_AddModuleRef() was added to Python 3.13 and limited C API 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline PyObject* PyImport_AddModuleRef(const char *name) { return Py_XNewRef(PyImport_AddModule(name)); @@ -528,8 +670,17 @@ static inline PyObject* PyImport_AddModuleRef(const char *name) #endif -// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D0000 +// gh-105927 added PyWeakref_GetRef() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) + +#if (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13) \ + && PY_VERSION_HEX >= _Py_PACK_VERSION(3, 15)) +// Function removed from Python 3.15 C API, but it remains available +// in the stable ABI +PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref); +#endif + static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) { PyObject *obj; @@ -538,7 +689,11 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) PyErr_SetString(PyExc_TypeError, "expected a weakref"); return -1; } + +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS obj = PyWeakref_GetObject(ref); +_Py_COMP_DIAG_POP if (obj == NULL) { // SystemError if ref is NULL *pobj = NULL; @@ -560,7 +715,8 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) #endif // bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 -#if PY_VERSION_HEX < 0x030800B1 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 8) \ + || (defined(Py_LIMITED_API) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 12))) static inline Py_ssize_t PyVectorcall_NARGS(size_t n) { return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; @@ -568,14 +724,16 @@ static inline Py_ssize_t PyVectorcall_NARGS(size_t n) #endif -// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 -#if PY_VERSION_HEX < 0x030900A4 +// gh-105922 added PyObject_Vectorcall() to Python 3.9 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 9) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 12))) static inline PyObject* PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) { -#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) - // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 +#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 8) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) + // bpo-36974 added _PyObject_Vectorcall() to Python 3.8 return _PyObject_Vectorcall(callable, args, nargsf, kwnames); #else PyObject *posargs = NULL, *kwargs = NULL; @@ -593,7 +751,11 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); if (kwnames) { +#ifndef Py_LIMITED_API nkwargs = PyTuple_GET_SIZE(kwnames); +#else + nkwargs = PyTuple_Size(kwnames); +#endif } else { nkwargs = 0; @@ -605,7 +767,13 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, } if (nposargs) { for (i=0; i < nposargs; i++) { +#ifndef Py_LIMITED_API PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); +#else + if (PyTuple_SetItem(posargs, i, Py_NewRef(*args)) < 0) { + goto error; + } +#endif args++; } } @@ -617,7 +785,14 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, } for (i = 0; i < nkwargs; i++) { - PyObject *key = PyTuple_GET_ITEM(kwnames, i); +#ifndef Py_LIMITED_API + PyObject *key = PyTuple_GET_ITEM(kwnames, i); // borrowed ref +#else + PyObject *key = PyTuple_GetItem(kwnames, i); // borrowed ref + if (key == NULL) { + goto error; + } +#endif PyObject *value = *args; args++; if (PyDict_SetItem(kwargs, key, value) < 0) { @@ -644,13 +819,15 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, // gh-106521 added PyObject_GetOptionalAttr() and -// PyObject_GetOptionalAttrString() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// PyObject_GetOptionalAttrString() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) { - // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 -#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) + // bpo-32571 added _PyObject_LookupAttr() to Python 3.7 +#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 7) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) return _PyObject_LookupAttr(obj, attr_name, result); #else *result = PyObject_GetAttr(obj, attr_name); @@ -685,8 +862,9 @@ PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject ** // gh-106307 added PyObject_GetOptionalAttr() and -// PyMapping_GetOptionalItemString() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// PyMapping_GetOptionalItemString() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) { @@ -717,8 +895,8 @@ PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **resul #endif // gh-108511 added PyMapping_HasKeyWithError() and -// PyMapping_HasKeyStringWithError() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// PyMapping_HasKeyStringWithError() to Python 3.13 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) static inline int PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) { @@ -740,8 +918,9 @@ PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) // gh-108511 added PyObject_HasAttrWithError() and -// PyObject_HasAttrStringWithError() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// PyObject_HasAttrStringWithError() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) { @@ -763,8 +942,9 @@ PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) // gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() -// to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) { @@ -797,8 +977,9 @@ PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) #endif -// gh-106307 added PyModule_Add() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// gh-106307 added PyModule_Add() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyModule_Add(PyObject *mod, const char *name, PyObject *value) { @@ -809,15 +990,16 @@ PyModule_Add(PyObject *mod, const char *name, PyObject *value) #endif -// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 +// gh-108014 added Py_IsFinalizing() to Python 3.13 (limited C API). // bpo-1856 added _Py_Finalizing to Python 3.2.1b1. // _Py_IsFinalizing() was added to PyPy 7.3.0. -#if (PY_VERSION_HEX < 0x030D00A1) \ - && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +#if ((PY_VERSION_HEX < _Py_PACK_VERSION(3, 13)) \ + && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) \ + && !defined(Py_LIMITED_API)) static inline int Py_IsFinalizing(void) { -#if PY_VERSION_HEX >= 0x030700A1 - // _Py_IsFinalizing() was added to Python 3.7.0a1. +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 7) + // _Py_IsFinalizing() was added to Python 3.7. return _Py_IsFinalizing(); #else return (_Py_Finalizing != NULL); @@ -826,8 +1008,8 @@ static inline int Py_IsFinalizing(void) #endif -// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// gh-108323 added PyDict_ContainsString() to Python 3.13 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) || defined(Py_LIMITED_API) static inline int PyDict_ContainsString(PyObject *op, const char *key) { PyObject *key_obj = PyUnicode_FromString(key); @@ -841,11 +1023,12 @@ static inline int PyDict_ContainsString(PyObject *op, const char *key) #endif -// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// gh-108445 added PyLong_AsInt() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyLong_AsInt(PyObject *obj) { -#ifdef PYPY_VERSION +#if defined(PYPY_VERSION) || defined(Py_LIMITED_API) long value = PyLong_AsLong(obj); if (value == -1 && PyErr_Occurred()) { return -1; @@ -863,8 +1046,8 @@ static inline int PyLong_AsInt(PyObject *obj) #endif -// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// gh-107073 added PyObject_VisitManagedDict() to Python 3.13 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) && !defined(Py_LIMITED_API) static inline int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) { @@ -887,9 +1070,9 @@ PyObject_ClearManagedDict(PyObject *obj) } #endif -// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 +// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13 // Python 3.5.2 added _PyThreadState_UncheckedGet(). -#if PY_VERSION_HEX < 0x030D00A1 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) && !defined(Py_LIMITED_API) static inline PyThreadState* PyThreadState_GetUnchecked(void) { @@ -898,12 +1081,16 @@ PyThreadState_GetUnchecked(void) #endif // gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() -// to Python 3.13.0a1 -#if PY_VERSION_HEX < 0x030D00A1 +// to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) { Py_ssize_t len; +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) + PyObject *bytes; +#endif const void *utf8; PyObject *exc_type, *exc_value, *exc_tb; int res; @@ -911,12 +1098,35 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_ // API cannot report errors so save/restore the exception PyErr_Fetch(&exc_type, &exc_value, &exc_tb); +#ifndef Py_LIMITED_API + if (PyUnicode_READY(unicode) < 0) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + if (PyUnicode_IS_ASCII(unicode)) { utf8 = PyUnicode_DATA(unicode); len = PyUnicode_GET_LENGTH(unicode); } - else { - // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() + else +#endif + { +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) + // Limited C API 3.9 and older + bytes = PyUnicode_AsUTF8String(unicode); + if (bytes == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + utf8 = PyBytes_AsString(bytes); + len = PyBytes_Size(bytes); +#else + // Python 3.12 and older, and limited C API 3.10 and newer. + // Python 3.10 added PyUnicode_AsUTF8AndSize() to the limited C API. utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); if (utf8 == NULL) { // Memory allocation failure. The API cannot report error, @@ -924,13 +1134,18 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_ res = 0; goto done; } +#endif } if (len != str_len) { res = 0; - goto done; } - res = (memcmp(utf8, str, (size_t)len) == 0); + else { + res = (memcmp(utf8, str, (size_t)len) == 0); + } +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) + Py_DECREF(bytes); +#endif done: PyErr_Restore(exc_type, exc_value, exc_tb); @@ -945,8 +1160,8 @@ PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) #endif -// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 -#if PY_VERSION_HEX < 0x030D00A2 +// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) || defined(Py_LIMITED_API) static inline int PyList_Extend(PyObject *list, PyObject *iterable) { @@ -960,8 +1175,8 @@ PyList_Clear(PyObject *list) } #endif -// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 -#if PY_VERSION_HEX < 0x030D00A2 +// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) || defined(Py_LIMITED_API) static inline int PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) { @@ -975,10 +1190,10 @@ PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) return -1; } - // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. - // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. - // Python 3.13.0a1 removed _PyDict_Pop(). -#if defined(PYPY_VERSION) || PY_VERSION_HEX >= 0x030D0000 + // bpo-16991 added _PyDict_Pop() to Python 3.5. + // Python 3.6 changed _PyDict_Pop() first argument type to PyObject*. + // Python 3.13 removed _PyDict_Pop(). +#if defined(PYPY_VERSION) || PY_VERSION_HEX >= _Py_PACK_VERSION(3, 13) || defined(Py_LIMITED_API) value = PyObject_CallMethod(dict, "pop", "O", key); #else value = _PyDict_Pop(dict, key, NULL); @@ -1020,22 +1235,47 @@ PyDict_PopString(PyObject *dict, const char *key, PyObject **result) #endif -// gh-111545 added Py_HashPointer() to Python 3.13.0a3 -#if PY_VERSION_HEX < 0x030D00A3 +// gh-111545 added Py_HashPointer() to Python 3.13 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) || defined(Py_LIMITED_API) static inline Py_hash_t Py_HashPointer(const void *ptr) { -#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 9) && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) return _Py_HashPointer(ptr); -#else +#elif !defined(Py_LIMITED_API) + // PyPy _Py_HashPointer() argument type is "void*", not "const void*" return _Py_HashPointer(_Py_CAST(void*, ptr)); +#else + // Limited C API implementation + uintptr_t x = (uintptr_t)ptr; + Py_BUILD_ASSERT(sizeof(x) == sizeof(ptr)); + + // Bottom 3 or 4 bits are likely to be 0; rotate x by 4 to the right + // to avoid excessive hash collisions for dicts and sets. + x = (x >> 4) | (x << (8 * sizeof(uintptr_t) - 4)); + + if (x == (uintptr_t)-1) { + x = (uintptr_t)-2; + } + + Py_BUILD_ASSERT(sizeof(x) == sizeof(Py_hash_t)); + return (Py_hash_t)x; #endif } #endif +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) || defined(Py_LIMITED_API) +Py_hash_t +PyObject_GenericHash(PyObject *obj) +{ + return Py_HashPointer(obj); +} +#endif + + // Python 3.13a4 added a PyTime API. // Use the private API added to Python 3.5. -#if PY_VERSION_HEX < 0x030D00A4 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) && !defined(Py_LIMITED_API) typedef _PyTime_t PyTime_t; #define PyTime_MIN _PyTime_MIN #define PyTime_MAX _PyTime_MAX @@ -1051,9 +1291,9 @@ static inline int PyTime_Time(PyTime_t *result) static inline int PyTime_PerfCounter(PyTime_t *result) { -#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 7) && !defined(PYPY_VERSION) return _PyTime_GetPerfCounterWithInfo(result, NULL); -#elif PY_VERSION_HEX >= 0x03070000 +#elif PY_VERSION_HEX >= _Py_PACK_VERSION(3, 7) // Call time.perf_counter_ns() and convert Python int object to PyTime_t. // Cache time.perf_counter_ns() function for best performance. static PyObject *func = NULL; @@ -1120,33 +1360,37 @@ static inline int PyTime_PerfCounter(PyTime_t *result) #endif -// gh-111389 added hash constants to Python 3.13.0a5. These constants were -// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8. +// gh-111389 added hash constants to Python 3.13. These constants were +// added first as private macros to Python 3.4 and PyPy 7.3.8. #if (!defined(PyHASH_BITS) \ - && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ - || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ - && PYPY_VERSION_NUM >= 0x07030800))) -# define PyHASH_BITS _PyHASH_BITS -# define PyHASH_MODULUS _PyHASH_MODULUS -# define PyHASH_INF _PyHASH_INF -# define PyHASH_IMAG _PyHASH_IMAG + && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= _Py_PACK_VERSION(3, 4)) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX >= _Py_PACK_VERSION(3, 7) \ + && PYPY_VERSION_NUM >= _Py_PACK_FULL_VERSION(7, 3, 8, 0, 0))) \ + && !defined(Py_LIMITED_API)) +# define PyHASH_BITS _PyHASH_BITS +# define PyHASH_MODULUS _PyHASH_MODULUS +# define PyHASH_INF _PyHASH_INF +# define PyHASH_IMAG _PyHASH_IMAG #endif // gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() -// to Python 3.13.0a6 -#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) - -#define Py_CONSTANT_NONE 0 -#define Py_CONSTANT_FALSE 1 -#define Py_CONSTANT_TRUE 2 -#define Py_CONSTANT_ELLIPSIS 3 -#define Py_CONSTANT_NOT_IMPLEMENTED 4 -#define Py_CONSTANT_ZERO 5 -#define Py_CONSTANT_ONE 6 -#define Py_CONSTANT_EMPTY_STR 7 -#define Py_CONSTANT_EMPTY_BYTES 8 -#define Py_CONSTANT_EMPTY_TUPLE 9 +// to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) + +#ifndef Py_CONSTANT_NONE +# define Py_CONSTANT_NONE 0 +# define Py_CONSTANT_FALSE 1 +# define Py_CONSTANT_TRUE 2 +# define Py_CONSTANT_ELLIPSIS 3 +# define Py_CONSTANT_NOT_IMPLEMENTED 4 +# define Py_CONSTANT_ZERO 5 +# define Py_CONSTANT_ONE 6 +# define Py_CONSTANT_EMPTY_STR 7 +# define Py_CONSTANT_EMPTY_BYTES 8 +# define Py_CONSTANT_EMPTY_TUPLE 9 +#endif static inline PyObject* Py_GetConstant(unsigned int constant_id) { @@ -1210,8 +1454,9 @@ static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) #endif -// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 -#if PY_VERSION_HEX < 0x030D00A4 +// gh-114329 added PyList_GetItemRef() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) static inline PyObject * PyList_GetItemRef(PyObject *op, Py_ssize_t index) { @@ -1222,8 +1467,9 @@ PyList_GetItemRef(PyObject *op, Py_ssize_t index) #endif -// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 -#if PY_VERSION_HEX < 0x030D00A4 +// gh-114329 added PyList_GetItemRef() to Python 3.13 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 15))) static inline int PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, PyObject **result) @@ -1262,14 +1508,15 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, } #endif -#if PY_VERSION_HEX < 0x030D00B3 -# define Py_BEGIN_CRITICAL_SECTION(op) { -# define Py_END_CRITICAL_SECTION() } -# define Py_BEGIN_CRITICAL_SECTION2(a, b) { -# define Py_END_CRITICAL_SECTION2() } +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } #endif -#if PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) typedef struct PyUnicodeWriter PyUnicodeWriter; static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) @@ -1407,8 +1654,7 @@ PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, Py_ssize_t start, Py_ssize_t end) { if (!PyUnicode_Check(str)) { - PyErr_Format(PyExc_TypeError, "expect str, not %s", - Py_TYPE(str)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expect str, not ", str); return -1; } if (start < 0 || start > end) { @@ -1424,6 +1670,21 @@ PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, start, end); } +static inline int +PyUnicodeWriter_WriteUCS4(PyUnicodeWriter *writer, + const Py_UCS4 *ucs4, Py_ssize_t size) +{ + PyObject *str = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, + ucs4, size); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + static inline int PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) { @@ -1439,104 +1700,198 @@ PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) Py_DECREF(str); return res; } -#endif // PY_VERSION_HEX < 0x030E0000 +#endif // PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) -// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 -#if PY_VERSION_HEX < 0x030E00A0 +// gh-116560 added PyLong_GetSign() to Python 3.14 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) || defined(Py_LIMITED_API) static inline int PyLong_GetSign(PyObject *obj, int *sign) { +#ifndef Py_LIMITED_API if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expect int, got ", obj); return -1; } *sign = _PyLong_Sign(obj); return 0; +#else + PyObject *zero = Py_GetConstant(Py_CONSTANT_ZERO); + int cmp; + + if (!PyLong_Check(obj)) { + PYTHONCAPI_COMPAT_EXPECT_TYPE("expect int, got ", obj); + return -1; + } + + cmp = PyObject_RichCompareBool(obj, zero, Py_GT); + if (cmp > 0) { + *sign = 1; + return 0; + } + if (cmp < 0) { + return -1; + } + + cmp = PyObject_RichCompareBool(obj, zero, Py_LT); + if (cmp > 0) { + *sign = -1; + return 0; + } + if (cmp < 0) { + return -1; + } + *sign = 0; + return 0; +#endif } #endif -// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2 -#if PY_VERSION_HEX < 0x030E00A2 +// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) || defined(Py_LIMITED_API) static inline int PyLong_IsPositive(PyObject *obj) { +#ifndef Py_LIMITED_API if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expected int, got ", obj); return -1; } return _PyLong_Sign(obj) == 1; +#else + PyObject *zero = Py_GetConstant(Py_CONSTANT_ZERO); + + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expect int, got %R", obj); + return -1; + } + + return PyObject_RichCompareBool(obj, zero, Py_GT); +#endif } static inline int PyLong_IsNegative(PyObject *obj) { +#ifndef Py_LIMITED_API if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expected int, got ", obj); return -1; } return _PyLong_Sign(obj) == -1; +#else + PyObject *zero = Py_GetConstant(Py_CONSTANT_ZERO); + int cmp; + + if (!PyLong_Check(obj)) { + PYTHONCAPI_COMPAT_EXPECT_TYPE("expect int, got ", obj); + return -1; + } + + cmp = PyObject_RichCompareBool(obj, zero, Py_LT); + if (cmp > 0) { + return 1; + } + if (cmp < 0) { + return -1; + } + return 0; +#endif } static inline int PyLong_IsZero(PyObject *obj) { +#ifndef Py_LIMITED_API if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expected int, got ", obj); return -1; } return _PyLong_Sign(obj) == 0; +#else + PyObject *zero = Py_GetConstant(Py_CONSTANT_ZERO); + + if (!PyLong_Check(obj)) { + PYTHONCAPI_COMPAT_EXPECT_TYPE("expect int, got ", obj); + return -1; + } + + return PyObject_RichCompareBool(obj, zero, Py_EQ); +#endif } #endif -// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 -#if PY_VERSION_HEX < 0x030E00A0 +// gh-124502 added PyUnicode_Equal() to Python 3.14 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 14))) -#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 13) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); #endif static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) { if (!PyUnicode_Check(str1)) { - PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", - Py_TYPE(str1)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("first argument must be str, not ", str1); return -1; } if (!PyUnicode_Check(str2)) { - PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", - Py_TYPE(str2)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("second argument must be str, not ", str2); return -1; } -#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 13) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) return _PyUnicode_Equal(str1, str2); -#elif !defined(PYPY_VERSION) +#elif !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) return _PyUnicode_EQ(str1, str2); -#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) +#elif (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 9) \ + && defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) return _PyUnicode_EQ(str1, str2); #else - return (PyUnicode_Compare(str1, str2) == 0); + { + int cmp = PyUnicode_Compare(str1, str2); + if (cmp == -1 && PyErr_Occurred()) { + return -1; + } + return (cmp == 0); + } #endif } #endif -// gh-121645 added PyBytes_Join() to Python 3.14.0a0 -#if PY_VERSION_HEX < 0x030E00A0 +// gh-121645 added PyBytes_Join() to Python 3.14 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) || defined(Py_LIMITED_API) static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) { +#ifndef Py_LIMITED_API return _PyBytes_Join(sep, iterable); +#else + if (sep == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + if (!PyBytes_Check(sep)) { + PyErr_Format(PyExc_TypeError, + "sep: expected bytes, got %T", sep); + return NULL; + } + + return PyObject_CallMethod(sep, "join", "O", iterable); +#endif } #endif -#if PY_VERSION_HEX < 0x030E00A0 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 16))) -#ifndef PYPY_VERSION +#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); #endif static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) { -#ifndef PYPY_VERSION +#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) return _Py_HashBytes(ptr, len); #else Py_hash_t hash; @@ -1552,19 +1907,25 @@ static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) #endif -#if PY_VERSION_HEX < 0x030E00A0 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 14))) static inline int PyIter_NextItem(PyObject *iter, PyObject **item) { + // Limited API: PyType_GetSlot() only accepts all types since Python 3.10 +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 10) iternextfunc tp_iternext; assert(iter != NULL); assert(item != NULL); +#ifndef Py_LIMITED_API tp_iternext = Py_TYPE(iter)->tp_iternext; +#else + tp_iternext = _Py_CAST(iternextfunc, PyType_GetSlot(Py_TYPE(iter), Py_tp_iternext)); +#endif if (tp_iternext == NULL) { *item = NULL; - PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", - Py_TYPE(iter)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expected an iterator, got ", iter); return -1; } @@ -1579,11 +1940,32 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item) return 0; } return -1; +#else + // Implementation for limited C API 3.9 and older + + assert(iter != NULL); + assert(item != NULL); + + // Cannot check if iter is an iterator: PyIter_Check() is only implemented + // as a function since Python 3.8. + + *item = PyIter_Next(iter); + if (*item != NULL) { + return 1; + } + else if (!PyErr_Occurred()) { + return 0; + } + else { + return -1; + } +#endif } #endif -#if PY_VERSION_HEX < 0x030E00A0 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 14))) static inline PyObject* PyLong_FromInt32(int32_t value) { Py_BUILD_ASSERT(sizeof(long) >= 4); @@ -1661,15 +2043,16 @@ static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) #endif -// gh-102471 added import and export API for integers to 3.14.0a2. -#if PY_VERSION_HEX < 0x030E00A2 && !defined(PYPY_VERSION) +// gh-102471 added import and export API for integers to 3.14.0. +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) // Helpers to access PyLongObject internals. static inline void _PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) { -#if PY_VERSION_HEX >= 0x030C0000 +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 12) op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3); -#elif PY_VERSION_HEX >= 0x030900A4 +#elif PY_VERSION_HEX >= _Py_PACK_VERSION(3, 9) Py_SET_SIZE(op, sign * size); #else Py_SIZE(op) = sign * size; @@ -1679,7 +2062,7 @@ _PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) static inline Py_ssize_t _PyLong_DigitCount(const PyLongObject *op) { -#if PY_VERSION_HEX >= 0x030C0000 +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 12) return (Py_ssize_t)(op->long_value.lv_tag >> 3); #else return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); @@ -1689,7 +2072,7 @@ _PyLong_DigitCount(const PyLongObject *op) static inline digit* _PyLong_GetDigits(const PyLongObject *op) { -#if PY_VERSION_HEX >= 0x030C0000 +#if PY_VERSION_HEX >= _Py_PACK_VERSION(3, 12) return (digit*)(op->long_value.ob_digit); #else return (digit*)(op->ob_digit); @@ -1731,8 +2114,7 @@ PyLong_Export(PyObject *obj, PyLongExport *export_long) { if (!PyLong_Check(obj)) { memset(export_long, 0, sizeof(*export_long)); - PyErr_Format(PyExc_TypeError, "expected int, got %s", - Py_TYPE(obj)->tp_name); + PYTHONCAPI_COMPAT_EXPECT_TYPE("expected int, got ", obj); return -1; } @@ -1840,55 +2222,60 @@ PyLongWriter_Finish(PyLongWriter *writer) #endif -#if PY_VERSION_HEX < 0x030C00A3 -# define Py_T_SHORT 0 -# define Py_T_INT 1 -# define Py_T_LONG 2 -# define Py_T_FLOAT 3 -# define Py_T_DOUBLE 4 -# define Py_T_STRING 5 -# define _Py_T_OBJECT 6 -# define Py_T_CHAR 7 -# define Py_T_BYTE 8 -# define Py_T_UBYTE 9 -# define Py_T_USHORT 10 -# define Py_T_UINT 11 -# define Py_T_ULONG 12 -# define Py_T_STRING_INPLACE 13 -# define Py_T_BOOL 14 -# define Py_T_OBJECT_EX 16 -# define Py_T_LONGLONG 17 -# define Py_T_ULONGLONG 18 -# define Py_T_PYSSIZET 19 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 12) +# define Py_T_SHORT 0 +# define Py_T_INT 1 +# define Py_T_LONG 2 +# define Py_T_FLOAT 3 +# define Py_T_DOUBLE 4 +# define Py_T_STRING 5 +# define _Py_T_OBJECT 6 +# define Py_T_CHAR 7 +# define Py_T_BYTE 8 +# define Py_T_UBYTE 9 +# define Py_T_USHORT 10 +# define Py_T_UINT 11 +# define Py_T_ULONG 12 +# define Py_T_STRING_INPLACE 13 +# define Py_T_BOOL 14 +# define Py_T_OBJECT_EX 16 +# define Py_T_LONGLONG 17 +# define Py_T_ULONGLONG 18 +# define Py_T_PYSSIZET 19 -# ifndef PYPY_VERSION -# define _Py_T_NONE 20 -# endif +# ifndef PYPY_VERSION +# define _Py_T_NONE 20 +# endif -# define Py_READONLY 1 -# define Py_AUDIT_READ 2 -# define _Py_WRITE_RESTRICTED 4 +# define Py_READONLY 1 +# define Py_AUDIT_READ 2 +# define _Py_WRITE_RESTRICTED 4 #endif // gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4 -#if PY_VERSION_HEX < 0x030E00A4 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) || defined(Py_LIMITED_API) -#ifndef PYPY_VERSION +#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode); #endif static inline FILE* Py_fopen(PyObject *path, const char *mode) { -#ifndef PYPY_VERSION +#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) return _Py_fopen_obj(path, mode); #else FILE *f; PyObject *bytes; + const char *path_bytes; if (!PyUnicode_FSConverter(path, &bytes)) { return NULL; } - const char *path_bytes = PyBytes_AS_STRING(bytes); +#ifndef Py_LIMITED_API + path_bytes = PyBytes_AS_STRING(bytes); +#else + path_bytes = PyBytes_AsString(bytes); +#endif f = fopen(path_bytes, mode); Py_DECREF(bytes); @@ -1908,7 +2295,11 @@ static inline int Py_fclose(FILE *file) #endif -#if 0x03080000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) +#if (_Py_PACK_VERSION(3, 9) <= PY_VERSION_HEX \ + && PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) + +// Use the internal C API added to Python 3.9 PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); static inline PyObject* @@ -1944,20 +2335,20 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"), PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"), PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL), -#if 0x030C0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 12) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"), PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL), -#if 0x03090000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 9) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"), #endif PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"), PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"), PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL), -#if 0x030B0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 11) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"), #endif PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL), @@ -1967,16 +2358,16 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), -#if 0x030B0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 11) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL), -#if 0x030D0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 13) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL), -#if 0x030B0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 11) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL), #endif #ifdef Py_GIL_DISABLED @@ -1994,19 +2385,19 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL), -#if 0x030A0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 10) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"), #endif PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL), -#if 0x030C0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 12) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), -#if 0x030B0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 11) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL), @@ -2015,12 +2406,12 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL), -#if 0x030B0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 11) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), -#if 0x030A0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 10) <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL), #endif }; @@ -2128,12 +2519,13 @@ PyConfig_GetInt(const char *name, int *value) *value = as_int; return 0; } -#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) +#endif // PY_VERSION_HEX > _Py_PACK_VERSION(3, 9) && !defined(PYPY_VERSION) -// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14.0b1. +// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14. // Adapted from _PyObject_IsUniquelyReferenced() implementation. // Not available on PyPy: Py_REFCNT() semantics is different on PyPy. -#if PY_VERSION_HEX < 0x030E00B0 && !defined(PYPY_VERSION) +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) static inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj) { #if !defined(Py_GIL_DISABLED) @@ -2150,9 +2542,9 @@ static inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj) #endif // gh-128926 added PyUnstable_TryIncRef() and PyUnstable_EnableTryIncRef() to -// Python 3.14.0a5. Adapted from _Py_TryIncref() and _PyObject_SetMaybeWeakref(). +// Python 3.14. Adapted from _Py_TryIncref() and _PyObject_SetMaybeWeakref(). // Not available on PyPy: Py_REFCNT() semantics is different on PyPy. -#if PY_VERSION_HEX < 0x030E00A5 && !defined(PYPY_VERSION) +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) && !defined(PYPY_VERSION) static inline int PyUnstable_TryIncRef(PyObject *op) { #ifndef Py_GIL_DISABLED @@ -2223,10 +2615,11 @@ static inline void PyUnstable_EnableTryIncRef(PyObject *op) (void)op; // unused argument #endif } -#endif // PY_VERSION_HEX < 0x030E00A5 && !defined(PYPY_VERSION) +#endif // PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) && !defined(PYPY_VERSION) -#if PY_VERSION_HEX < 0x030F0000 +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 15))) static inline PyObject* PySys_GetAttrString(const char *name) { @@ -2243,12 +2636,29 @@ PySys_GetAttrString(const char *name) static inline PyObject* PySys_GetAttr(PyObject *name) { +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) + // Limited C API 3.9 and older + PyObject *bytes, *result; + bytes = PyUnicode_AsUTF8String(name); + if (bytes == NULL) { + return NULL; + } + result = PySys_GetAttrString(PyBytes_AsString(bytes)); + Py_DECREF(bytes); + return result; +#else + // Python 3.14 and newer, and limited C API 3.10 and newer +#ifndef Py_LIMITED_API const char *name_str = PyUnicode_AsUTF8(name); +#else + const char *name_str = PyUnicode_AsUTF8AndSize(name, NULL); +#endif if (name_str == NULL) { return NULL; } return PySys_GetAttrString(name_str); +#endif } static inline int @@ -2264,18 +2674,37 @@ PySys_GetOptionalAttrString(const char *name, PyObject **value) static inline int PySys_GetOptionalAttr(PyObject *name, PyObject **value) { +#if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 10) + // Limited C API 3.9 and older + PyObject *bytes; + int res; + bytes = PyUnicode_AsUTF8String(name); + if (bytes == NULL) { + *value = NULL; + return -1; + } + res = PySys_GetOptionalAttrString(PyBytes_AsString(bytes), value); + Py_DECREF(bytes); + return res; +#else +#ifndef Py_LIMITED_API const char *name_str = PyUnicode_AsUTF8(name); +#else + const char *name_str = PyUnicode_AsUTF8AndSize(name, NULL); +#endif if (name_str == NULL) { *value = NULL; return -1; } return PySys_GetOptionalAttrString(name_str, value); +#endif } -#endif // PY_VERSION_HEX < 0x030F00A1 +#endif // PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) -#if PY_VERSION_HEX < 0x030F00A1 +// _PyBytes_Resize() is not available in the limited C API. +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) && !defined(Py_LIMITED_API) typedef struct PyBytesWriter { char small_buffer[256]; PyObject *obj; @@ -2404,7 +2833,8 @@ PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size) result = PyBytes_FromStringAndSize("", 0); } else if (writer->obj != NULL) { - if (size != PyBytes_GET_SIZE(writer->obj)) { + Py_ssize_t old_size = PyBytes_GET_SIZE(writer->obj); + if (size != old_size) { if (_PyBytes_Resize(&writer->obj, size)) { goto error; } @@ -2530,10 +2960,10 @@ PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) Py_DECREF(str); return res; } -#endif // PY_VERSION_HEX < 0x030F00A1 +#endif // PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) -#if PY_VERSION_HEX < 0x030F00A1 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) || defined(Py_LIMITED_API) static inline PyObject* PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) { @@ -2543,14 +2973,21 @@ PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) } for (Py_ssize_t i=0; i < size; i++) { PyObject *item = array[i]; +#ifndef Py_LIMITED_API PyTuple_SET_ITEM(tuple, i, Py_NewRef(item)); +#else + if (PyTuple_SetItem(tuple, i, Py_NewRef(item)) < 0) { + Py_DECREF(tuple); + return NULL; + } +#endif } return tuple; } #endif -#if PY_VERSION_HEX < 0x030F00A1 +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) && !defined(Py_LIMITED_API) static inline Py_hash_t PyUnstable_Unicode_GET_CACHED_HASH(PyObject *op) { @@ -2563,12 +3000,14 @@ PyUnstable_Unicode_GET_CACHED_HASH(PyObject *op) } #endif -#if 0x030D0000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030F00A7 && !defined(PYPY_VERSION) +#if (_Py_PACK_VERSION(3, 13) <= PY_VERSION_HEX \ + && PY_VERSION_HEX < _Py_PACK_VERSION(3, 15) \ + && !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)) // Immortal objects were implemented in Python 3.12, however there is no easy API // to make objects immortal until 3.14 which has _Py_SetImmortal(). Since // immortal objects are primarily needed for free-threading, this API is implemented // for 3.14 using _Py_SetImmortal() and uses private macros on 3.13. -#if 0x030E0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 14) <= PY_VERSION_HEX PyAPI_FUNC(void) _Py_SetImmortal(PyObject *op); #endif @@ -2579,7 +3018,7 @@ PyUnstable_SetImmortal(PyObject *op) if (!PyUnstable_Object_IsUniquelyReferenced(op) || PyUnicode_Check(op)) { return 0; } -#if 0x030E0000 <= PY_VERSION_HEX +#if _Py_PACK_VERSION(3, 14) <= PY_VERSION_HEX _Py_SetImmortal(op); #else // Python 3.13 doesn't export _Py_SetImmortal() function @@ -2598,6 +3037,101 @@ PyUnstable_SetImmortal(PyObject *op) } #endif + +// Python 3.14 added PyImport_ImportModuleAttr() +// and PyImport_ImportModuleAttrString() +#if PY_VERSION_HEX < _Py_PACK_VERSION(3, 14) || defined(Py_LIMITED_API) +static inline PyObject* +PyImport_ImportModuleAttr(PyObject *mod_name, PyObject *attr_name) +{ + PyObject *mod = PyImport_Import(mod_name); + if (mod == NULL) { + return NULL; + } + + PyObject *result = PyObject_GetAttr(mod, attr_name); + Py_DECREF(mod); + return result; +} + +static inline PyObject * +PyImport_ImportModuleAttrString(const char *mod_name, const char *attr_name) +{ + PyObject *mod_name_obj = PyUnicode_FromString(mod_name); + if (mod_name_obj == NULL) { + return NULL; + } + PyObject *attr_name_obj = PyUnicode_FromString(attr_name); + if (attr_name_obj == NULL) { + Py_DECREF(mod_name_obj); + return NULL; + } + + PyObject *result = PyImport_ImportModuleAttr(mod_name_obj, attr_name_obj); + Py_DECREF(attr_name_obj); + Py_DECREF(mod_name_obj); + return result; +} +#endif + + +// Python 3.11 added PyType_GetName() and PyType_GetQualName() +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 11) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 11))) +static inline PyObject* +PyType_GetName(PyTypeObject *type) +{ + return PyObject_GetAttrString(_PyObject_CAST(type), "__name__"); +} + +static inline PyObject* +PyType_GetQualName(PyTypeObject *type) +{ + return PyObject_GetAttrString(_PyObject_CAST(type), "__qualname__"); +} +#endif + + +// Python 3.13 added PyType_GetModuleName() and PyType_GetFullyQualifiedName() +#if (PY_VERSION_HEX < _Py_PACK_VERSION(3, 13) \ + || (defined(Py_LIMITED_API) && Py_LIMITED_API+0 < _Py_PACK_VERSION(3, 13))) +static inline PyObject* +PyType_GetModuleName(PyTypeObject *type) +{ + return PyObject_GetAttrString(_PyObject_CAST(type), "__module__"); +} + +static inline PyObject * +PyType_GetFullyQualifiedName(PyTypeObject *type) +{ + PyObject *qualname = PyType_GetQualName(type); + if (qualname == NULL) { + return NULL; + } + + PyObject *module = PyType_GetModuleName(type); + if (module == NULL) { + Py_DECREF(qualname); + return NULL; + } + + PyObject *result; + if (PyUnicode_Check(module) + && PyUnicode_EqualToUTF8(module, "builtins") == 0 + && PyUnicode_EqualToUTF8(module, "__main__") == 0) + { + result = PyUnicode_FromFormat("%U.%U", module, qualname); + } + else { + result = Py_NewRef(qualname); + } + Py_DECREF(module); + Py_DECREF(qualname); + return result; +} +#endif + + #ifdef __cplusplus } #endif