├── .github └── workflows │ └── build.yml ├── .gitignore ├── .readthedocs.yaml ├── CODE_OF_CONDUCT.md ├── COPYING ├── README.rst ├── docs ├── Makefile ├── api.rst ├── changelog.rst ├── conf.py ├── index.rst ├── links.rst ├── make.bat ├── tests.rst ├── upgrade.rst └── users.rst ├── pythoncapi_compat.h ├── runtests.py ├── tests ├── __init__.py ├── setup.py ├── test_pythoncapi_compat.py ├── test_pythoncapi_compat_cext.c ├── test_pythoncapi_compat_cppext.cpp ├── test_upgrade_pythoncapi.py └── utils.py └── upgrade_pythoncapi.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | # Available OS Images: 15 | # https://github.com/actions/runner-images/#available-images 16 | # 17 | # Allow Python pre-releases: 18 | # https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#allow-pre-releases 19 | # See "allow-prereleases: true" below. 20 | os: [ubuntu-latest] 21 | python: 22 | # Python versions (CPython): 23 | # https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json 24 | - "3.8" 25 | - "3.9" 26 | - "3.10" 27 | - "3.11" 28 | - "3.12" 29 | - "3.13" 30 | # CPython 3.14 final is scheduled for October 2025: 31 | # https://peps.python.org/pep-0719/ 32 | - "3.14" 33 | 34 | # PyPy versions: 35 | # - https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md 36 | # - https://downloads.python.org/pypy/ 37 | # - Only versions listed in this JSON are supported: 38 | # https://downloads.python.org/pypy/versions.json 39 | - "pypy2.7" 40 | - "pypy3.6" 41 | - "pypy3.7" 42 | - "pypy3.8" 43 | - "pypy3.9" 44 | - "pypy3.10" 45 | - "pypy3.11" 46 | 47 | # Old PyPy versions 48 | # See https://foss.heptapod.net/pypy/pypy/-/issues/3991 49 | - "pypy2.7-v7.3.2" 50 | - "pypy3.6-v7.3.2" 51 | - "pypy3.7-v7.3.2" 52 | 53 | include: 54 | # Windows 55 | - os: windows-latest 56 | python: "3.6" 57 | - os: windows-latest 58 | python: "3.7" 59 | - os: windows-latest 60 | python: "3.8" 61 | - os: windows-latest 62 | python: "3.9" 63 | - os: windows-latest 64 | python: "3.10" 65 | - os: windows-latest 66 | python: "3.11" 67 | - os: windows-latest 68 | python: "3.12" 69 | - os: windows-latest 70 | python: "3.13" 71 | 72 | # macOS 73 | # Python 3.9 is the oldest version available on macOS/arm64. 74 | - os: macos-latest 75 | python: "3.9" 76 | - os: macos-latest 77 | python: "3.10" 78 | - os: macos-latest 79 | python: "3.11" 80 | - os: macos-latest 81 | python: "3.12" 82 | - os: macos-latest 83 | python: "3.13" 84 | 85 | # Ubuntu: test deadsnakes Python versions which are not supported by 86 | # GHA python-versions. 87 | - os: ubuntu-22.04 88 | python: "3.7" 89 | 90 | steps: 91 | # https://github.com/actions/checkout 92 | - uses: actions/checkout@v4 93 | - name: Setup Python 94 | # https://github.com/actions/setup-python 95 | uses: actions/setup-python@v5 96 | with: 97 | python-version: ${{ matrix.python }} 98 | allow-prereleases: true 99 | - name: Install setuptools 100 | run: python -m pip install setuptools 101 | - name: Display the Python version 102 | run: python -VV 103 | - name: Run tests 104 | run: python runtests.py --current --verbose 105 | 106 | test_python27: 107 | # Get Python 2.7 from Ubuntu 22.04. 108 | # 109 | # Python 2.7 was removed from GHA setup-python in June 2023: 110 | # https://github.com/actions/setup-python/issues/672 111 | name: 'Test Python 2.7' 112 | runs-on: ubuntu-22.04 113 | steps: 114 | - uses: actions/checkout@v4 115 | - name: Install Python 2.7 116 | run: | 117 | sudo apt-get update 118 | sudo apt-get -yq install python2.7 python2.7-dev 119 | - name: Display the Python version 120 | run: python2.7 -VV 121 | - name: Run tests 122 | run: python2.7 runtests.py --current --verbose 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.swp 3 | tests/build/ 4 | *.py[cod] 5 | __pycache__ 6 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3.12" 10 | 11 | sphinx: 12 | configuration: docs/conf.py 13 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Please note that all interactions on 4 | [Python Software Foundation](https://www.python.org/psf-landing/)-supported 5 | infrastructure is [covered](https://www.python.org/psf/records/board/minutes/2014-01-06/#management-of-the-psfs-web-properties) 6 | by the [PSF Code of Conduct](https://www.python.org/psf/codeofconduct/), 7 | which includes all the infrastructure used in the development of Python itself 8 | (e.g. mailing lists, issue trackers, GitHub, etc.). 9 | 10 | In general, this means that everyone is expected to be **open**, **considerate**, and 11 | **respectful** of others no matter what their position is within the project. 12 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | BSD Zero Clause License 2 | 3 | Copyright Contributors to the pythoncapi_compat project. 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ++++++++++++++++++++++++++ 2 | Python C API compatibility 3 | ++++++++++++++++++++++++++ 4 | 5 | .. image:: https://github.com/python/pythoncapi-compat/actions/workflows/build.yml/badge.svg 6 | :alt: Build status of pythoncapi-compat on GitHub Actions 7 | :target: https://github.com/python/pythoncapi-compat/actions 8 | 9 | The ``pythoncapi-compat`` project can be used to write a C or C++ extension 10 | supporting a wide range of Python versions with a single code base. It is made 11 | of the ``pythoncapi_compat.h`` header file and the ``upgrade_pythoncapi.py`` 12 | script. 13 | 14 | ``upgrade_pythoncapi.py`` requires Python 3.6 or newer. 15 | 16 | See the `documentation at ReadTheDocs 17 | `_ 18 | for more details. 19 | 20 | Getting started 21 | =============== 22 | 23 | To upgrade a specific file:: 24 | 25 | python3 upgrade_pythoncapi.py module.c 26 | 27 | To upgrade all C/C++ files in a directory:: 28 | 29 | python3 upgrade_pythoncapi.py src/ 30 | 31 | Select operations 32 | ----------------- 33 | 34 | To only replace ``op->ob_type`` with ``Py_TYPE(op)``, select the ``Py_TYPE`` 35 | operation with:: 36 | 37 | python3 upgrade_pythoncapi.py -o Py_TYPE module.c 38 | 39 | Or the opposite, to apply all operations but leave ``op->ob_type`` unchanged, 40 | deselect the ``Py_TYPE`` operation with:: 41 | 42 | python3 upgrade_pythoncapi.py -o all,-Py_TYPE module.c 43 | 44 | Download pythoncapi_compat.h 45 | ---------------------------- 46 | 47 | If you want to ``pythoncapi_compat.h`` to your code base, use the 48 | ``upgrade_pythoncapi.py`` tool to fetch it:: 49 | 50 | python3 upgrade_pythoncapi.py --download PATH 51 | 52 | 53 | This project is distributed under the `Zero Clause BSD (0BSD) license 54 | `_ and is covered by the `PSF Code of 55 | Conduct `_. 56 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | +++++++++++++++++++++++ 2 | pythoncapi_compat.h API 3 | +++++++++++++++++++++++ 4 | 5 | The ``pythoncapi_compat.h`` header file provides implementations of recent 6 | functions for old Python versions. 7 | 8 | Supported Python versions: 9 | 10 | * Python 3.6 - 3.14 11 | * PyPy 2.7 and PyPy 3.6 - 3.10 12 | 13 | Python 2.7 and Python 3.5 are no longer officially supported since GitHub 14 | Actions doesn't support them anymore: only best effort support is provided. 15 | 16 | C++03 and C++11 are supported on Python 3.6 and newer. 17 | 18 | A C11 subset (without optional features) is required, like ``static inline`` 19 | functions: see `PEP 7 `_. ISO C90 20 | is partially supported for Python 2.7. 21 | 22 | Some functions related to frame objects and ``PyThreadState`` are not available 23 | on PyPy. 24 | 25 | Latest version of the header file: 26 | `pythoncapi_compat.h `_. 27 | 28 | 29 | Python 3.15 30 | ----------- 31 | 32 | .. c:function:: PyObject* PySys_GetAttr(const char *name) 33 | 34 | See `PySys_GetAttr() documentation `__. 35 | 36 | .. c:function:: PyObject* PySys_GetAttrString(const char *name) 37 | 38 | See `PySys_GetAttrString() documentation `__. 39 | 40 | .. c:function:: PyObject* PySys_GetOptionalAttr(const char *name) 41 | 42 | See `PySys_GetOptionalAttr() documentation `__. 43 | 44 | .. c:function:: PyObject* PySys_GetOptionalAttrString(const char *name) 45 | 46 | See `PySys_GetOptionalAttrString() documentation `__. 47 | 48 | Python 3.14 49 | ----------- 50 | 51 | .. c:struct:: PyLongLayout 52 | 53 | See `PyLongLayout documentation `__. 54 | 55 | .. c:function:: const PyLongLayout* PyLong_GetNativeLayout(void) 56 | 57 | See `PyLong_GetNativeLayout() documentation `__. 58 | 59 | .. c:struct:: PyLongExport 60 | 61 | See `PyLongExport documentation `__. 62 | 63 | .. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long) 64 | 65 | See `PyLong_Export() documentation `__. 66 | 67 | .. c:function:: void PyLong_FreeExport(PyLongExport *export_long) 68 | 69 | See `PyLong_FreeExport() documentation `__. 70 | 71 | .. c:struct:: PyLongWriter 72 | 73 | See `PyLongWriter documentation `__. 74 | 75 | .. c:function:: PyLongWriter* PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) 76 | 77 | See `PyLongWriter_Create() documentation `__. 78 | 79 | .. c:function:: PyObject* PyLongWriter_Finish(PyLongWriter *writer) 80 | 81 | See `PyLongWriter_Finish() documentation `__. 82 | 83 | .. c:function:: void PyLongWriter_Discard(PyLongWriter *writer) 84 | 85 | See `PyLongWriter_Discard() documentation `__. 86 | 87 | .. c:function:: int PyLong_IsPositive(PyObject *obj) 88 | 89 | See `PyLong_IsPositive() documentation `__. 90 | 91 | .. c:function:: int PyLong_IsNegative(PyObject *obj) 92 | 93 | See `PyLong_IsNegative() documentation `__. 94 | 95 | .. c:function:: int PyLong_IsZero(PyObject *obj) 96 | 97 | See `PyLong_IsZero() documentation `__. 98 | 99 | .. c:function:: int PyLong_GetSign(PyObject *obj, int *sign) 100 | 101 | See `PyLong_GetSign() documentation `__. 102 | 103 | .. c:function:: PyObject* PyIter_NextItem(PyObject *sep, PyObject *iterable) 104 | 105 | See `PyIter_NextItem() documentation `__. 106 | 107 | .. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) 108 | 109 | See `PyBytes_Join() documentation `__. 110 | 111 | .. c:function:: Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) 112 | 113 | See `Py_HashBuffer() documentation `__. 114 | 115 | .. c:function:: int PyUnicode_Equal(PyObject *str1, PyObject *str2) 116 | 117 | See `PyUnicode_Equal() documentation `__. 118 | 119 | .. c:function:: PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) 120 | 121 | See `PyUnicodeWriter_Create() documentation `__. 122 | 123 | .. c:function:: PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) 124 | 125 | See `PyUnicodeWriter_Finish() documentation `__. 126 | 127 | .. c:function:: void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) 128 | 129 | See `PyUnicodeWriter_Discard() documentation `__. 130 | 131 | .. c:function:: int PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) 132 | 133 | See `PyUnicodeWriter_WriteChar() documentation `__. 134 | 135 | .. c:function:: int PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, const char *str, Py_ssize_t size) 136 | 137 | See `PyUnicodeWriter_WriteUTF8() documentation `__. 138 | 139 | .. c:function:: int PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, const wchar_t *str, Py_ssize_t size) 140 | 141 | See `PyUnicodeWriter_WriteWideChar() documentation `__. 142 | 143 | .. c:function:: int PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) 144 | 145 | See `PyUnicodeWriter_WriteStr() documentation `__. 146 | 147 | .. c:function:: int PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) 148 | 149 | See `PyUnicodeWriter_WriteRepr() documentation `__. 150 | 151 | .. c:function:: int PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, Py_ssize_t start, Py_ssize_t end) 152 | 153 | See `PyUnicodeWriter_WriteSubstring() documentation `__. 154 | 155 | .. c:function:: int PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) 156 | 157 | See `PyUnicodeWriter_Format() documentation `__. 158 | 159 | .. c:function:: int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) 160 | 161 | See `PyLong_AsInt32() documentation `__. 162 | 163 | .. c:function:: int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) 164 | 165 | See `PyLong_AsInt64() documentation `__. 166 | 167 | .. c:function:: int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) 168 | 169 | See `PyLong_AsUInt32() documentation `__. 170 | 171 | .. c:function:: int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) 172 | 173 | See `PyLong_AsUInt64() documentation `__. 174 | 175 | .. c:function:: PyObject* PyLong_FromInt32(int32_t value) 176 | 177 | See `PyLong_FromInt32() documentation `__. 178 | 179 | .. c:function:: PyObject* PyLong_FromInt64(int64_t value) 180 | 181 | See `PyLong_FromInt64() documentation `__. 182 | 183 | .. c:function:: PyObject* PyLong_FromUInt32(uint32_t value) 184 | 185 | See `PyLong_FromUInt32() documentation `__. 186 | 187 | .. c:function:: PyObject* PyLong_FromUInt64(uint64_t value) 188 | 189 | See `PyLong_FromUInt64() documentation `__. 190 | 191 | .. c:function:: FILE* Py_fopen(PyObject *path, const char *mode) 192 | 193 | See `Py_fopen() documentation `__. 194 | 195 | .. c:function:: int Py_fclose(FILE *file) 196 | 197 | See `Py_fclose() documentation `__. 198 | 199 | .. c:function:: PyObject* PyConfig_Get(const char *name) 200 | 201 | See `PyConfig_Get() documentation `__. 202 | 203 | .. c:function:: int PyConfig_GetInt(const char *name, int *value) 204 | 205 | See `PyConfig_GetInt() documentation `__. 206 | 207 | 208 | Not supported: 209 | 210 | * ``PyConfig_Names()`` 211 | * ``PyConfig_Set()`` 212 | * ``PyInitConfig_AddModule()`` 213 | * ``PyInitConfig_Create()`` 214 | * ``PyInitConfig_Free()`` 215 | * ``PyInitConfig_FreeStrList()`` 216 | * ``PyInitConfig_GetError()`` 217 | * ``PyInitConfig_GetExitCode()`` 218 | * ``PyInitConfig_GetInt()`` 219 | * ``PyInitConfig_GetStr()`` 220 | * ``PyInitConfig_GetStrList()`` 221 | * ``PyInitConfig_HasOption()`` 222 | * ``PyInitConfig_SetInt()`` 223 | * ``PyInitConfig_SetStr()`` 224 | * ``PyInitConfig_SetStrList()`` 225 | * ``PyType_GetBaseByToken()`` 226 | * ``PyUnicodeWriter_DecodeUTF8Stateful()`` 227 | * ``Py_InitializeFromInitConfig()`` 228 | 229 | 230 | Python 3.13 231 | ----------- 232 | 233 | .. c:function:: int PyDict_GetItemRef(PyObject *p, PyObject *key, PyObject **result) 234 | 235 | See `PyDict_GetItemRef() documentation `__. 236 | 237 | .. c:function:: int PyDict_GetItemStringRef(PyObject *p, const char *key, PyObject **result) 238 | 239 | See `PyDict_GetItemStringRef() documentation `__. 240 | 241 | .. c:function:: PyObject* PyImport_AddModuleRef(const char *name) 242 | 243 | See `PyImport_AddModuleRef() documentation `__. 244 | 245 | .. c:function:: int PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) 246 | 247 | See `PyObject_GetOptionalAttr() documentation `__. 248 | 249 | .. c:function:: int PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) 250 | 251 | See `PyObject_GetOptionalAttrString() documentation `__. 252 | 253 | .. c:function:: int PyObject_HasAttrWithError(PyObject *obj, PyObject *attr_name) 254 | 255 | See `PyObject_HasAttrWithError() documentation `__. 256 | 257 | .. c:function:: int PyObject_HasAttrStringWithError(PyObject *obj, const char *attr_name) 258 | 259 | See `PyObject_HasAttrStringWithError() documentation `__. 260 | 261 | .. c:function:: int PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) 262 | 263 | See `PyMapping_GetOptionalItem() documentation `__. 264 | 265 | .. c:function:: int PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) 266 | 267 | See `PyMapping_GetOptionalItemString() documentation `__. 268 | 269 | .. c:function:: int PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) 270 | 271 | See `PyMapping_HasKeyWithError() documentation `__. 272 | 273 | .. c:function:: int PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) 274 | 275 | See `PyMapping_HasKeyStringWithError() documentation `__. 276 | 277 | .. c:function:: int PyModule_Add(PyObject *module, const char *name, PyObject *value) 278 | 279 | See `PyModule_Add() documentation `__. 280 | 281 | .. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) 282 | 283 | See `PyWeakref_GetRef() documentation `__. 284 | 285 | .. c:function:: int Py_IsFinalizing() 286 | 287 | Return non-zero if the Python interpreter is shutting down, return 0 288 | otherwise. 289 | 290 | Availability: Python 3.3 and newer, PyPy 7.3 and newer. 291 | 292 | See `Py_IsFinalizing() documentation `__. 293 | 294 | .. c:function:: int PyDict_ContainsString(PyObject *p, const char *key) 295 | 296 | See `PyDict_ContainsString() documentation `__. 297 | 298 | .. c:function:: int PyLong_AsInt(PyObject *obj) 299 | 300 | See `PyLong_AsInt() documentation `__. 301 | 302 | .. c:function:: int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) 303 | 304 | See `PyObject_VisitManagedDict() documentation `__. 305 | 306 | .. c:function:: void PyObject_ClearManagedDict(PyObject *obj) 307 | 308 | See `PyObject_ClearManagedDict() documentation `__. 309 | 310 | .. c:function:: PyThreadState* PyThreadState_GetUnchecked(void) 311 | 312 | See `PyThreadState_GetUnchecked() documentation `__. 313 | 314 | Available on Python 3.5.2 and newer. 315 | 316 | .. c:function:: int PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) 317 | 318 | See `PyUnicode_EqualToUTF8() documentation `__. 319 | 320 | .. c:function:: int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t size) 321 | 322 | See `PyUnicode_EqualToUTF8AndSize() documentation `__. 323 | 324 | .. c:function:: int PyList_Extend(PyObject *list, PyObject *iterable) 325 | 326 | See `PyList_Extend() documentation `__. 327 | 328 | .. c:function:: int PyList_Clear(PyObject *list) 329 | 330 | See `PyList_Clear() documentation `__. 331 | 332 | .. c:function:: int PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) 333 | 334 | See `PyDict_Pop() documentation `__. 335 | 336 | .. c:function:: int PyDict_PopString(PyObject *dict, const char *key, PyObject **result) 337 | 338 | See `PyDict_PopString() documentation `__. 339 | 340 | .. c:function:: Py_hash_t Py_HashPointer(const void *ptr) 341 | 342 | See `Py_HashPointer() documentation `__. 343 | 344 | .. c:type:: PyTime_t 345 | 346 | A timestamp or duration in nanoseconds, represented as a signed 64-bit 347 | integer. 348 | 349 | .. c:var:: PyTime_t PyTime_MIN 350 | 351 | Minimum value of :c:type:`PyTime_t`. 352 | 353 | .. c:var:: PyTime_t PyTime_MAX 354 | 355 | Maximum value of :c:type:`PyTime_t`. 356 | 357 | .. c:function:: double PyTime_AsSecondsDouble(PyTime_t t) 358 | 359 | See `PyTime_AsSecondsDouble() documentation `__. 360 | 361 | .. c:function:: int PyTime_Monotonic(PyTime_t *result) 362 | 363 | See `PyTime_Monotonic() documentation `__. 364 | 365 | .. c:function:: int PyTime_Time(PyTime_t *result) 366 | 367 | See `PyTime_Time() documentation `__. 368 | 369 | .. c:function:: int PyTime_PerfCounter(PyTime_t *result) 370 | 371 | See `PyTime_PerfCounter() documentation `__. 372 | 373 | .. c:function:: PyObject* PyList_GetItemRef(PyObject *op, Py_ssize_t index) 374 | 375 | See `PyList_GetItemRef() documentation `__. 376 | 377 | .. c:function:: int PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, PyObject **result) 378 | 379 | See `PyDict_SetDefaultRef() documentation `__. 380 | 381 | 382 | Not supported: 383 | 384 | * ``PyErr_FormatUnraisable()``. 385 | * ``PyLong_AsNativeBytes()`` 386 | * ``PyLong_FromNativeBytes()`` 387 | * ``PyLong_FromUnsignedNativeBytes()`` 388 | * ``PyObject_GenericHash()``. 389 | * ``PySys_Audit()``. 390 | * ``PySys_AuditTuple()``. 391 | * ``PyType_GetFullyQualifiedName()`` 392 | * ``PyType_GetModuleName()`` 393 | 394 | Python 3.12 395 | ----------- 396 | 397 | .. c:function:: PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) 398 | 399 | See `PyFrame_GetVar() documentation `__. 400 | 401 | Not available on PyPy. 402 | 403 | .. c:function:: PyObject* PyFrame_GetVarString(PyFrameObject *frame, const char *name) 404 | 405 | See `PyFrame_GetVarString() documentation `__. 406 | 407 | Not available on PyPy. 408 | 409 | .. c:function:: PyObject* Py_GetConstant(unsigned int constant_id) 410 | 411 | See `Py_GetConstant() documentation `__. 412 | 413 | .. c:function:: PyObject* Py_GetConstantBorrowed(unsigned int constant_id) 414 | 415 | See `Py_GetConstantBorrowed() documentation `__. 416 | 417 | 418 | Not supported: 419 | 420 | * ``PyDict_AddWatcher()``, ``PyDict_Watch()``. 421 | * ``PyCode_AddWatcher()``, ``PyCode_ClearWatcher()``. 422 | * ``PyErr_GetRaisedException()``, ``PyErr_SetRaisedException()``. 423 | * ``_PyErr_ChainExceptions1()``. 424 | * ``PyErr_DisplayException()``. 425 | * ``_Py_IsImmortal()``. 426 | * ``Py_NewInterpreterFromConfig()``. 427 | * ``PyException_GetArgs()``, ``PyException_SetArgs()``. 428 | * ``PyEval_SetProfileAllThreads()``, ``PyEval_SetTraceAllThreads()``. 429 | * ``PyFunction_SetVectorcall()``. 430 | * ``PyType_FromMetaclass()``: implementation too big to be backported. 431 | * ``PyVectorcall_Call()``. 432 | 433 | Python 3.11 434 | ----------- 435 | 436 | .. c:function:: PyObject* PyCode_GetCellvars(PyCodeObject *code) 437 | 438 | See `PyCode_GetCellvars() documentation `__. 439 | 440 | Not available on PyPy. 441 | 442 | .. c:function:: PyObject* PyCode_GetCode(PyCodeObject *code) 443 | 444 | See `PyCode_GetCode() documentation `__. 445 | 446 | Not available on PyPy. 447 | 448 | .. c:function:: PyObject* PyCode_GetFreevars(PyCodeObject *code) 449 | 450 | See `PyCode_GetFreevars() documentation `__. 451 | 452 | Not available on PyPy. 453 | 454 | .. c:function:: PyObject* PyCode_GetVarnames(PyCodeObject *code) 455 | 456 | See `PyCode_GetVarnames() documentation `__. 457 | 458 | Not available on PyPy. 459 | 460 | .. c:function:: PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) 461 | 462 | See `PyFrame_GetBuiltins() documentation `__. 463 | 464 | Not available on PyPy. 465 | 466 | .. c:function:: PyObject* PyFrame_GetGlobals(PyFrameObject *frame) 467 | 468 | See `PyFrame_GetGlobals() documentation `__. 469 | 470 | Not available on PyPy. 471 | 472 | .. c:function:: int PyFrame_GetLasti(PyFrameObject *frame) 473 | 474 | See `PyFrame_GetLasti() documentation `__. 475 | 476 | Not available on PyPy. 477 | 478 | .. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame) 479 | 480 | See `PyFrame_GetLocals() documentation `__. 481 | 482 | Not available on PyPy. 483 | 484 | .. c:function:: void PyThreadState_EnterTracing(PyThreadState *tstate) 485 | 486 | See `PyThreadState_EnterTracing() documentation `__. 487 | 488 | Not available on PyPy. 489 | 490 | .. c:function:: void PyThreadState_LeaveTracing(PyThreadState *tstate) 491 | 492 | See `PyThreadState_LeaveTracing() documentation `__. 493 | 494 | Not available on PyPy 495 | 496 | .. c:function:: int PyFloat_Pack2(double x, unsigned char *p, int le) 497 | 498 | Pack a C double as the IEEE 754 binary16 half-precision format. 499 | 500 | Availability: Python 3.6 and newer. Not available on PyPy 501 | 502 | .. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le) 503 | 504 | Pack a C double as the IEEE 754 binary32 single precision format. 505 | 506 | Not available on PyPy 507 | 508 | .. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le) 509 | 510 | Pack a C double as the IEEE 754 binary64 double precision format. 511 | 512 | Not available on PyPy 513 | 514 | .. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le) 515 | 516 | Unpack the IEEE 754 binary16 half-precision format as a C double. 517 | 518 | Availability: Python 3.6 and newer. Not available on PyPy 519 | 520 | .. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le) 521 | 522 | Unpack the IEEE 754 binary32 single precision format as a C double. 523 | 524 | Not available on PyPy 525 | 526 | .. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le) 527 | 528 | Unpack the IEEE 754 binary64 double precision format as a C double. 529 | 530 | Not available on PyPy 531 | 532 | Not supported: 533 | 534 | * ``PyType_GetModuleByDef()``. 535 | * ``PyType_GetName()``. 536 | * ``PyType_GetQualName()``. 537 | * ``Py_Version`` constant. 538 | * ``PyErr_GetHandledException()``, ``PyErr_SetHandledException()``. 539 | * ``PyFrame_GetGenerator()``. 540 | 541 | Python 3.10 542 | ----------- 543 | 544 | .. c:function:: PyObject* Py_NewRef(PyObject *obj) 545 | 546 | See `Py_NewRef() documentation `__. 547 | 548 | .. c:function:: PyObject* Py_XNewRef(PyObject *obj) 549 | 550 | See `Py_XNewRef() documentation `__. 551 | 552 | .. c:function:: int Py_Is(PyObject *x, PyObject *y) 553 | 554 | See `Py_Is() documentation `__. 555 | 556 | .. c:function:: int Py_IsNone(PyObject *x) 557 | 558 | See `Py_IsNone() documentation `__. 559 | 560 | .. c:function:: int Py_IsTrue(PyObject *x) 561 | 562 | See `Py_IsTrue() documentation `__. 563 | 564 | .. c:function:: int Py_IsFalse(PyObject *x) 565 | 566 | See `Py_IsFalse() documentation `__. 567 | 568 | .. c:function:: int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) 569 | 570 | See `PyModule_AddObjectRef() documentation `__. 571 | 572 | Not supported: 573 | 574 | * ``PyCodec_Unregister()``. 575 | * ``PyDateTime_DATE_GET_TZINFO()``, ``PyDateTime_TIME_GET_TZINFO()``. 576 | * ``PyErr_SetInterruptEx()``. 577 | * ``PyGC_Enable()``, ``PyGC_Disable()`` and ``PyGC_IsEnabled()``. 578 | * ``PyIter_Send()``. 579 | * ``PySet_CheckExact()``. 580 | * ``Py_TPFLAGS_DISALLOW_INSTANTIATION`` constant. 581 | * ``Py_TPFLAGS_IMMUTABLETYPE`` constant. 582 | 583 | Python 3.9 584 | ---------- 585 | 586 | PyObject 587 | ^^^^^^^^ 588 | 589 | .. c:function:: void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) 590 | 591 | See `Py_SET_REFCNT() documentation `__. 592 | 593 | .. c:function:: void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) 594 | 595 | See `Py_SET_TYPE() documentation `__. 596 | 597 | .. c:function:: void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) 598 | 599 | See `Py_SET_SIZE() documentation `__. 600 | 601 | .. c:function:: int Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) 602 | 603 | See `Py_IS_TYPE() documentation `__. 604 | 605 | .. c:function:: PyObject* PyObject_CallNoArgs(PyObject *func) 606 | 607 | See `PyObject_CallNoArgs() documentation `__. 608 | 609 | .. c:function:: PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) 610 | 611 | See `PyObject_CallOneArg() documentation `__. 612 | 613 | .. c:function:: PyObject* PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) 614 | 615 | See `PyObject_Vectorcall() documentation `__. 616 | 617 | .. c:function:: Py_ssize_t PyVectorcall_NARGS(size_t nargsf) 618 | 619 | See `PyVectorcall_NARGS() documentation `__. 620 | 621 | .. c:macro:: PY_VECTORCALL_ARGUMENTS_OFFSET 622 | 623 | See `PY_VECTORCALL_ARGUMENTS_OFFSET documentation `__. 624 | 625 | Not supported: 626 | 627 | * ``PyVectorcall_CallMethod()``. 628 | * ``PyType_FromModuleAndSpec()`` 629 | 630 | 631 | 632 | PyFrameObject 633 | ^^^^^^^^^^^^^ 634 | 635 | .. c:function:: PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) 636 | 637 | See `PyFrame_GetCode() documentation `__. 638 | 639 | .. c:function:: PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) 640 | 641 | See `PyFrame_GetBack() documentation `__. 642 | 643 | Not available on PyPy 644 | 645 | 646 | PyThreadState 647 | ^^^^^^^^^^^^^ 648 | 649 | .. c:function:: PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) 650 | 651 | See `PyThreadState_GetFrame() documentation `__. 652 | 653 | Not available on PyPy 654 | 655 | .. c:function:: PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState *tstate) 656 | 657 | See `PyThreadState_GetInterpreter() documentation `__. 658 | 659 | .. c:function:: uint64_t PyThreadState_GetID(PyThreadState *tstate) 660 | 661 | See `PyThreadState_GetID() documentation `__. 662 | 663 | Availability: Python 3.7. Not available on PyPy. 664 | 665 | PyInterpreterState 666 | ^^^^^^^^^^^^^^^^^^ 667 | 668 | .. c:function:: PyInterpreterState* PyInterpreterState_Get(void) 669 | 670 | See `PyInterpreterState_Get() documentation `__. 671 | 672 | 673 | GC protocol 674 | ^^^^^^^^^^^ 675 | 676 | .. c:function:: int PyObject_GC_IsTracked(PyObject* obj) 677 | 678 | See `PyObject_GC_IsTracked() documentation `__. 679 | 680 | Not available on PyPy. 681 | 682 | .. c:function:: int PyObject_GC_IsFinalized(PyObject *obj) 683 | 684 | See `PyObject_GC_IsFinalized() documentation `__. 685 | 686 | Availability: Python 3.4. Not available on PyPy. 687 | 688 | Module helper 689 | ^^^^^^^^^^^^^ 690 | 691 | .. c:function:: int PyModule_AddType(PyObject *module, PyTypeObject *type) 692 | 693 | See `PyModule_AddType() documentation `__. 694 | 695 | Python 3.8 696 | ---------- 697 | 698 | Not supported: 699 | 700 | * ``PyCode_NewWithPosOnlyArgs()``. 701 | 702 | Python 3.7 703 | ---------- 704 | 705 | Not supported: 706 | 707 | * ``PyImport_GetModule()``. 708 | * ``PyInterpreterState_GetID()``. 709 | * ``PySlice_Unpack()``, ``PySlice_AdjustIndices()``. 710 | * ``PyTimeZone_FromOffset()``, ``PyTimeZone_FromOffsetAndName()``. 711 | * ``Py_RETURN_RICHCOMPARE()``. 712 | * ``Py_UNREACHABLE`` macro. 713 | 714 | Python 3.6 715 | ---------- 716 | 717 | Not supported: 718 | 719 | * ``PyErr_ResourceWarning()``. 720 | * ``PyErr_SetImportErrorSubclass()``. 721 | * ``PyOS_FSPath()``. 722 | * ``Py_FinalizeEx()``. 723 | 724 | Python 3.5.2 725 | ------------ 726 | 727 | .. c:macro:: Py_SETREF(op, op2) 728 | 729 | .. c:macro:: Py_XSETREF(op, op2) 730 | 731 | Not supported: 732 | 733 | * ``PyCodec_NameReplaceErrors()``. 734 | * ``PyErr_FormatV()``. 735 | * ``PyExc_RecursionError``. 736 | * ``PyModule_FromDefAndSpec()``, ``PyModule_FromDefAndSpec2()``, 737 | and ``PyModule_ExecDef()``. 738 | * ``PyNumber_MatrixMultiply()`` and ``PyNumber_InPlaceMatrixMultiply()``. 739 | 740 | Python 3.4 741 | ---------- 742 | 743 | .. c:macro:: Py_UNUSED(name) 744 | 745 | See `Py_UNUSED() documentation `__. 746 | 747 | Python 3.2 748 | ---------- 749 | 750 | Not supported: 751 | 752 | * ``Py_VA_COPY``. 753 | * ``PySys_SetArgvEx()``. 754 | * ``PyLong_AsLongLongAndOverflow()``. 755 | * ``PyErr_NewExceptionWithDoc()``. 756 | 757 | Python 3.1 758 | ---------- 759 | 760 | Not supported: 761 | 762 | * ``PyOS_string_to_double()``. 763 | * ``PyCapsule`` API. 764 | 765 | Borrow variant 766 | -------------- 767 | 768 | To ease migration of C extensions to the new C API, a variant is provided 769 | to return borrowed references rather than strong references. 770 | 771 | These functions are only available in ``pythoncapi_compat.h`` and are not 772 | part of the Python C API. 773 | 774 | .. c:function:: PyObject* _Py_StealRef(PyObject *ob) 775 | 776 | Similar to ``Py_DECREF(ob); return ob;``. 777 | 778 | .. c:function:: PyObject* _Py_XStealRef(PyObject *ob) 779 | 780 | Similar to ``Py_XDECREF(ob); return ob;``. 781 | 782 | .. c:function:: PyFrameObject* _PyThreadState_GetFrameBorrow(PyThreadState *tstate) 783 | 784 | :c:func:`PyThreadState_GetFrame` variant. Not available on PyPy. 785 | 786 | .. c:function:: PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) 787 | 788 | :c:func:`PyFrame_GetCode` variant. 789 | 790 | .. c:function:: PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) 791 | 792 | :c:func:`PyFrame_GetBack` variant Not available on PyPy. 793 | 794 | For example, ``tstate->frame`` can be replaced with 795 | ``_PyThreadState_GetFrameBorrow(tstate)`` to avoid accessing directly 796 | ``PyThreadState.frame`` member. 797 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | * 2025-06-03: Add functions: 5 | 6 | * ``PySys_GetAttr()`` 7 | * ``PySys_GetAttrString()`` 8 | * ``PySys_GetOptionalAttr()`` 9 | * ``PySys_GetOptionalAttrString()`` 10 | 11 | * 2025-01-19: Add ``PyConfig_Get()`` functions. 12 | * 2025-01-06: Add ``Py_fopen()`` and ``Py_fclose()`` functions. 13 | * 2024-12-16: Add ``structmember.h`` constants: 14 | 15 | * ``Py_T_BOOL`` 16 | * ``Py_T_BYTE`` 17 | * ``Py_T_CHAR`` 18 | * ``Py_T_DOUBLE`` 19 | * ``Py_T_FLOAT`` 20 | * ``Py_T_INT`` 21 | * ``Py_T_LONGLONG`` 22 | * ``Py_T_LONG`` 23 | * ``Py_T_OBJECT_EX`` 24 | * ``Py_T_PYSSIZET`` 25 | * ``Py_T_SHORT`` 26 | * ``Py_T_STRING_INPLACE`` 27 | * ``Py_T_STRING`` 28 | * ``Py_T_UBYTE`` 29 | * ``Py_T_UINT`` 30 | * ``Py_T_ULONGLONG`` 31 | * ``Py_T_ULONG`` 32 | * ``Py_T_USHORT`` 33 | * ``_Py_T_NONE`` 34 | * ``_Py_T_OBJECT`` 35 | * ``Py_AUDIT_READ`` 36 | * ``Py_READONLY`` 37 | * ``_Py_WRITE_RESTRICTED`` 38 | 39 | * 2024-12-13: Add functions and structs: 40 | 41 | * ``PyLongLayout`` 42 | * ``PyLong_GetNativeLayout()`` 43 | * ``PyLongExport`` 44 | * ``PyLong_Export()`` 45 | * ``PyLong_FreeExport()`` 46 | * ``PyLongWriter`` 47 | * ``PyLongWriter_Create()`` 48 | * ``PyLongWriter_Finish()`` 49 | * ``PyLongWriter_Discard()`` 50 | 51 | * 2024-11-12: Add functions: 52 | 53 | * ``PyLong_IsPositive()`` 54 | * ``PyLong_IsNegative()`` 55 | * ``PyLong_IsZero()`` 56 | 57 | * 2024-10-09: Add functions: 58 | 59 | * ``PyBytes_Join()`` 60 | * ``PyIter_NextItem()`` 61 | * ``PyLong_AsInt32()`` 62 | * ``PyLong_AsInt64()`` 63 | * ``PyLong_AsUInt32()`` 64 | * ``PyLong_AsUInt64()`` 65 | * ``PyLong_FromInt32()`` 66 | * ``PyLong_FromInt64()`` 67 | * ``PyLong_FromUInt32()`` 68 | * ``PyLong_FromUInt64()`` 69 | * ``PyUnicode_Equal()`` 70 | * ``Py_HashBuffer()`` 71 | 72 | * 2024-07-18: Add functions: 73 | 74 | * ``PyUnicodeWriter_Create()`` 75 | * ``PyUnicodeWriter_Discard()`` 76 | * ``PyUnicodeWriter_Finish()`` 77 | * ``PyUnicodeWriter_WriteChar()`` 78 | * ``PyUnicodeWriter_WriteUTF8()`` 79 | * ``PyUnicodeWriter_WriteStr()`` 80 | * ``PyUnicodeWriter_WriteRepr()`` 81 | * ``PyUnicodeWriter_WriteSubstring()`` 82 | * ``PyUnicodeWriter_WriteWideChar()`` 83 | * ``PyUnicodeWriter_Format()`` 84 | 85 | * 2024-06-03: Add ``PyLong_GetSign()``. 86 | * 2024-04-23: Drop Python 3.5 support. It cannot be tested anymore (pip fails). 87 | * 2024-04-02: Add ``PyDict_SetDefaultRef()`` function. 88 | * 2024-03-29: Add ``PyList_GetItemRef()`` function. 89 | * 2024-03-21: Add functions: 90 | 91 | * ``Py_GetConstant()`` 92 | * ``Py_GetConstantBorrowed()`` 93 | 94 | * 2024-03-09: Add hash constants: 95 | 96 | * ``PyHASH_BITS`` 97 | * ``PyHASH_IMAG`` 98 | * ``PyHASH_INF`` 99 | * ``PyHASH_MODULUS`` 100 | 101 | * 2024-02-20: Add PyTime API: 102 | 103 | * ``PyTime_t`` type 104 | * ``PyTime_MIN`` and ``PyTime_MAX`` constants 105 | * ``PyTime_AsSecondsDouble()`` 106 | * ``PyTime_Monotonic()`` 107 | * ``PyTime_PerfCounter()`` 108 | * ``PyTime_Time()`` 109 | 110 | * 2023-12-15: Add function ``Py_HashPointer()``. 111 | * 2023-11-14: Add functions: 112 | 113 | * ``PyDict_Pop()`` 114 | * ``PyDict_PopString()`` 115 | 116 | * 2023-11-13: Add functions: 117 | 118 | * ``PyList_Extend()`` 119 | * ``PyList_Clear()`` 120 | 121 | * 2023-10-04: Add functions: 122 | 123 | * ``PyUnicode_EqualToUTF8()`` 124 | * ``PyUnicode_EqualToUTF8AndSize()`` 125 | 126 | * 2023-10-03: Add functions: 127 | 128 | * ``PyObject_VisitManagedDict()`` 129 | * ``PyObject_ClearManagedDict()`` 130 | * ``PyThreadState_GetUnchecked()`` 131 | 132 | * 2023-09-29: Add functions: 133 | 134 | * ``PyMapping_HasKeyWithError()`` 135 | * ``PyMapping_HasKeyStringWithError()`` 136 | * ``PyObject_HasAttrWithError()`` 137 | * ``PyObject_HasAttrStringWithError()`` 138 | 139 | * 2023-08-25: Add ``PyDict_ContainsString()`` and ``PyLong_AsInt()`` functions. 140 | * 2023-08-21: Remove support for Python 2.7, Python 3.4 and older. 141 | * 2023-08-16: Add ``Py_IsFinalizing()`` function. 142 | * 2023-07-21: Add ``PyDict_GetItemRef()`` function. 143 | * 2023-07-18: Add ``PyModule_Add()`` function. 144 | * 2023-07-12: Add functions: 145 | 146 | * ``PyObject_GetOptionalAttr()`` 147 | * ``PyObject_GetOptionalAttrString()`` 148 | * ``PyMapping_GetOptionalItem()`` 149 | * ``PyMapping_GetOptionalItemString()`` 150 | 151 | * 2023-07-05: Add ``PyObject_Vectorcall()`` function. 152 | * 2023-06-21: Add ``PyWeakref_GetRef()`` function. 153 | * 2023-06-20: Add ``PyImport_AddModuleRef()`` function. 154 | * 2022-11-15: Add experimental operations to the ``upgrade_pythoncapi.py`` 155 | script: ``Py_NewRef``, ``Py_CLEAR`` and ``Py_SETREF``. 156 | * 2022-11-09: Fix ``Py_SETREF()`` and ``Py_XSETREF()`` macros 157 | for `gh-98724 `_. 158 | * 2022-11-04: Add ``PyFrame_GetVar()`` and ``PyFrame_GetVarString()`` 159 | functions. 160 | * 2022-08-04: Add ``PyCode_GetVarnames()``, ``PyCode_GetFreevars()`` 161 | and ``PyCode_GetCellvars()`` functions. 162 | * 2022-06-14: Fix compatibility with C++ older than C++11. 163 | * 2022-05-03: Add ``PyCode_GetCode()`` function. 164 | * 2022-04-26: Rename the project from ``pythoncapi_compat`` to 165 | ``pythoncapi-compat``: replace the underscore separator with a dash. 166 | * 2022-04-08: Add functions ``PyFrame_GetLocals()``, ``PyFrame_GetGlobals()`` 167 | ``PyFrame_GetBuiltins()``, and ``PyFrame_GetLasti()``. 168 | * 2022-03-12: Add functions ``PyFloat_Pack2()``, ``PyFloat_Pack4()``, 169 | ``PyFloat_Pack8()``, ``PyFloat_Unpack2()``, ``PyFloat_Unpack4()`` and 170 | ``PyFloat_Unpack8()``. 171 | * 2022-03-03: The project moved to https://github.com/python/pythoncapi-compat 172 | * 2022-02-11: The project license changes from the MIT license to the Zero 173 | Clause BSD (0BSD) license. Projects copying ``pythoncapi_compat.h`` no longer 174 | have to include the MIT license and the copyright notice. 175 | * 2022-02-08: Add documentation. 176 | * 2022-02-09: ``pythoncapi_compat.h`` now supports C++ on Python 3.6 and newer: 177 | use ``nullptr`` and ``reinterpret_cast`` cast on C++, and use ``NULL`` 178 | and ``(type)`` cast on C. 179 | * 2021-10-15: Add ``PyThreadState_EnterTracing()`` and 180 | ``PyThreadState_LeaveTracing()``. 181 | * 2021-04-09: Add ``Py_Is()``, ``Py_IsNone()``, ``Py_IsTrue()``, 182 | ``Py_IsFalse()`` functions. 183 | * 2021-04-01: 184 | 185 | * Add ``Py_SETREF()``, ``Py_XSETREF()`` and ``Py_UNUSED()``. 186 | * Add PyPy support. 187 | 188 | * 2021-01-27: Fix compatibility with Visual Studio 2008 for Python 2.7. 189 | * 2020-11-30: Creation of the ``upgrade_pythoncapi.py`` script. 190 | * 2020-06-04: Creation of the ``pythoncapi_compat.h`` header file. 191 | 192 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'pythoncapi_compat' 21 | copyright = '2022, Victor Stinner' 22 | author = 'Victor Stinner' 23 | language = "en" 24 | 25 | 26 | # -- General configuration --------------------------------------------------- 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | ] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['templates'] 36 | 37 | # List of patterns, relative to source directory, that match files and 38 | # directories to ignore when looking for source files. 39 | # This pattern also affects html_static_path and html_extra_path. 40 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 41 | 42 | 43 | # -- Options for HTML output ------------------------------------------------- 44 | 45 | # The theme to use for HTML and HTML Help pages. See the documentation for 46 | # a list of builtin themes. 47 | # 48 | html_theme = 'default' 49 | 50 | # Add any paths that contain custom static files (such as style sheets) here, 51 | # relative to this directory. They are copied after the builtin static files, 52 | # so a file named "default.css" will overwrite the builtin "default.css". 53 | html_static_path = ['static'] 54 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ++++++++++++++++++++++++++ 2 | Python C API compatibility 3 | ++++++++++++++++++++++++++ 4 | 5 | The ``pythoncapi-compat`` project can be used to write a C or C++ extension 6 | supporting a wide range of Python versions with a single code base. It is made 7 | of the ``pythoncapi_compat.h`` header file and the ``upgrade_pythoncapi.py`` 8 | script. 9 | 10 | * Homepage: `GitHub pythoncapi-compat project 11 | `_. 12 | * `Documentation 13 | `_ 14 | 15 | Documentation: 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | 20 | upgrade 21 | api 22 | users 23 | tests 24 | changelog 25 | links 26 | 27 | This project is distributed under the `Zero Clause BSD (0BSD) license 28 | `_ and is covered by the `PSF Code of 29 | Conduct `_. 30 | -------------------------------------------------------------------------------- /docs/links.rst: -------------------------------------------------------------------------------- 1 | Links 2 | ===== 3 | 4 | * `Python/C API Reference Manual `_ 5 | * `HPy: a better API for Python 6 | `_ 7 | * `Cython: C-extensions for Python 8 | `_ 9 | * `Old 2to3c project `_ by David Malcolm 10 | which uses `Coccinelle `_ 11 | to ease migration of C extensions from Python 2 to Python 3. See 12 | also `2to3c: an implementation of Python's 2to3 for C code 13 | `_ article (2009). 14 | * PEPs related to the C API: 15 | 16 | * `PEP 620 -- Hide implementation details from the C API 17 | `_ 18 | * `PEP 670 -- Convert macros to functions in the Python C API 19 | `_ 20 | * `PEP 674 -- Disallow using macros as l-values 21 | `_ 22 | 23 | * Make structures opaque 24 | 25 | * `bpo-39573: PyObject `_ 26 | * `bpo-40170: PyTypeObject `_ 27 | * `bpo-39947: PyThreadState `_ 28 | * `bpo-40421: PyFrameObject `_ 29 | * `bpo-47241: PyCodeObject `_ 30 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/tests.rst: -------------------------------------------------------------------------------- 1 | Run tests 2 | ========= 3 | 4 | To run pythoncapi-compat tests, type:: 5 | 6 | python3 runtests.py 7 | 8 | To only test the current Python version, use the ``-c`` or the ``--current`` 9 | option:: 10 | 11 | python3 runtests.py --current 12 | 13 | Verbose mode (``-v``, ``--verbose``):: 14 | 15 | python3 runtests.py --verbose 16 | 17 | See tests in the ``tests/`` subdirectory. 18 | -------------------------------------------------------------------------------- /docs/upgrade.rst: -------------------------------------------------------------------------------- 1 | ++++++++++++++++++++++++++++ 2 | upgrade_pythoncapi.py script 3 | ++++++++++++++++++++++++++++ 4 | 5 | ``upgrade_pythoncapi.py`` requires Python 3.6 or newer. 6 | 7 | Usage 8 | ===== 9 | 10 | Run the with no arguments to display command line options and list available 11 | operations:: 12 | 13 | python3 upgrade_pythoncapi.py 14 | 15 | Select files and directories 16 | ---------------------------- 17 | 18 | To upgrade ``module.c`` file, type:: 19 | 20 | python3 upgrade_pythoncapi.py module.c 21 | 22 | To upgrade all C and C++ files (``.c``, ``.h``, ``.cc``, ``.cpp``, ``.cxx`` and 23 | ``.hpp`` files) in the ``directory/`` directory, type:: 24 | 25 | python3 upgrade_pythoncapi.py directory/ 26 | 27 | Multiple filenames an directories can be specified on the command line. 28 | 29 | Files are modified in-place! If a file is modified, a copy of the original file 30 | is created with the ``.old`` suffix. 31 | 32 | Select operations 33 | ----------------- 34 | 35 | To only replace ``op->ob_type`` with ``Py_TYPE(op)``, select the ``Py_TYPE`` 36 | operation with:: 37 | 38 | python3 upgrade_pythoncapi.py -o Py_TYPE module.c 39 | 40 | Or the opposite, to apply all operations but leave ``op->ob_type`` unchanged, 41 | deselect the ``Py_TYPE`` operation with:: 42 | 43 | python3 upgrade_pythoncapi.py -o all,-Py_TYPE module.c 44 | 45 | Download pythoncapi_compat.h 46 | ---------------------------- 47 | 48 | Most ``upgrade_pythoncapi.py`` operations add ``#include 49 | "pythoncapi_compat.h"``. You may have to download the ``pythoncapi_compat.h`` 50 | header file to your project. It can be downloaded with:: 51 | 52 | python3 upgrade_pythoncapi.py --download PATH 53 | 54 | 55 | Upgrade Operations 56 | ================== 57 | 58 | ``upgrade_pythoncapi.py`` implements the following operations: 59 | 60 | Py_TYPE 61 | ------- 62 | 63 | * Replace ``op->ob_type`` with ``Py_TYPE(op)``. 64 | 65 | Py_SIZE 66 | ------- 67 | 68 | * Replace ``op->ob_size`` with ``Py_SIZE(op)``. 69 | 70 | Py_REFCNT 71 | --------- 72 | 73 | * Replace ``op->ob_refcnt`` with ``Py_REFCNT(op)``. 74 | 75 | Py_SET_TYPE 76 | ----------- 77 | 78 | * Replace ``obj->ob_type = type;`` with ``Py_SET_TYPE(obj, type);``. 79 | * Replace ``Py_TYPE(obj) = type;`` with ``Py_SET_TYPE(obj, type);``. 80 | 81 | Py_SET_SIZE 82 | ----------- 83 | 84 | * Replace ``obj->ob_size = size;`` with ``Py_SET_SIZE(obj, size);``. 85 | * Replace ``Py_SIZE(obj) = size;`` with ``Py_SET_SIZE(obj, size);``. 86 | 87 | Py_SET_REFCNT 88 | ------------- 89 | 90 | * Replace ``obj->ob_refcnt = refcnt;`` with ``Py_SET_REFCNT(obj, refcnt);``. 91 | * Replace ``Py_REFCNT(obj) = refcnt;`` with ``Py_SET_REFCNT(obj, refcnt);``. 92 | 93 | Py_Is 94 | ----- 95 | 96 | * Replace ``x == Py_None`` with ``Py_IsNone(x)``. 97 | * Replace ``x == Py_True`` with ``Py_IsTrue(x)``. 98 | * Replace ``x == Py_False`` with ``Py_IsFalse(x)``. 99 | * Replace ``x != Py_None`` with ``!Py_IsNone(x)``. 100 | * Replace ``x != Py_True`` with ``!Py_IsTrue(x)``. 101 | * Replace ``x != Py_False`` with ``!Py_IsFalse(x)``. 102 | 103 | PyObject_NEW 104 | ------------ 105 | 106 | * Replace ``PyObject_NEW(...)`` with ``PyObject_New(...)``. 107 | * Replace ``PyObject_NEW_VAR(...)`` with ``PyObject_NewVar(...)``. 108 | 109 | PyMem_MALLOC 110 | ------------ 111 | 112 | * Replace ``PyMem_MALLOC(n)`` with ``PyMem_Malloc(n)``. 113 | * Replace ``PyMem_REALLOC(ptr, n)`` with ``PyMem_Realloc(ptr, n)``. 114 | * Replace ``PyMem_FREE(ptr)``, ``PyMem_DEL(ptr)`` and ``PyMem_Del(ptr)`` . 115 | with ``PyMem_Free(n)``. 116 | 117 | PyObject_MALLOC 118 | --------------- 119 | 120 | * Replace ``PyObject_MALLOC(n)`` with ``PyObject_Malloc(n)``. 121 | * Replace ``PyObject_REALLOC(ptr, n)`` with ``PyObject_Realloc(ptr, n)``. 122 | * Replace ``PyObject_FREE(ptr)``, ``PyObject_DEL(ptr)`` 123 | and ``PyObject_Del(ptr)`` . with ``PyObject_Free(n)``. 124 | 125 | PyFrame_GetBack 126 | --------------- 127 | 128 | * Replace ``frame->f_back`` with ``_PyFrame_GetBackBorrow(frame)``. 129 | 130 | PyFrame_GetCode 131 | --------------- 132 | 133 | * Replace ``frame->f_code`` with ``_PyFrame_GetCodeBorrow(frame)``. 134 | 135 | PyThreadState_GetInterpreter 136 | ---------------------------- 137 | 138 | * Replace ``tstate->interp`` with ``PyThreadState_GetInterpreter(tstate)``. 139 | 140 | PyThreadState_GetFrame 141 | ---------------------- 142 | 143 | * Replace ``tstate->frame`` with ``_PyThreadState_GetFrameBorrow(tstate)``. 144 | 145 | Experimental operations 146 | ----------------------- 147 | 148 | The following operations are experimental (ex: can introduce compiler warnings) 149 | and so not included in the ``all`` group, they have to be selected explicitly. 150 | Example: ``-o all,Py_SETREF``. 151 | 152 | Experimental operations: 153 | 154 | * ``Py_NewRef``: 155 | 156 | * Replace ``Py_INCREF(res); return res;`` with ``return Py_NewRef(res);`` 157 | * Replace ``x = y; Py_INCREF(x);`` with ``x = Py_NewRef(y);`` 158 | * Replace ``x = y; Py_INCREF(y);`` with ``x = Py_NewRef(y);`` 159 | * Replace ``Py_INCREF(y); x = y;`` with ``x = Py_NewRef(y);`` 160 | 161 | * ``Py_CLEAR``: 162 | 163 | * Replace ``Py_XDECREF(var); var = NULL;`` with ``Py_CLEAR(var);`` 164 | 165 | * ``Py_SETREF``: 166 | 167 | * Replace ``Py_DECREF(x); x = y;`` with ``Py_SETREF(x, y);`` 168 | * Replace ``Py_XDECREF(x); x = y;`` with ``Py_XSETREF(x, y);`` 169 | -------------------------------------------------------------------------------- /docs/users.rst: -------------------------------------------------------------------------------- 1 | +++++++++++++++++++++++ 2 | pythoncapi-compat users 3 | +++++++++++++++++++++++ 4 | 5 | Examples of projects using pythoncapi_compat.h 6 | ============================================== 7 | 8 | * `bitarray `_: 9 | ``bitarray/_bitarray.c`` uses ``Py_SET_SIZE()`` 10 | (`pythoncapi_compat.h copy 11 | `__) 12 | * `datatable `_ 13 | (`commit `__) 14 | * `guppy3 `_ 15 | (`commit 16 | `__) 17 | * `immutables `_: 18 | ``immutables/_map.c`` uses ``Py_SET_SIZE()`` 19 | (`pythoncapi_compat.h copy 20 | `__) 21 | * `Mercurial (hg) `_ uses ``Py_SET_TYPE()`` 22 | (`commit 23 | `__, 24 | `pythoncapi_compat.h copy 25 | `__) 26 | * `mypy `_ 27 | (mypyc, 28 | `commit `__) 29 | * `numpy `_: 30 | `pythoncapi-compat Git submodule 31 | `_ 32 | * `pybluez `_ 33 | (`commit `__) 34 | * `python-snappy `_ 35 | (`commit `__) 36 | * `python-zstandard `_ 37 | uses ``Py_SET_TYPE()`` and ``Py_SET_SIZE()`` 38 | (`commit `__): 39 | Mercurial extension. 40 | * `python-zstd `_ 41 | (`commit `__) 42 | * `hollerith `_ 43 | ``src/writer.c`` uses ``PyObject_CallOneArg() and other Python 3.9 apis`` 44 | (`pythoncapi_compat.h copy 45 | `__) 46 | * `PyTorch `_ (`pythoncapi_compat.h copy 47 | `__) 48 | * `PyGObject `_ 49 | (`commit `__, 50 | `pythoncapi-compat Meson subproject 51 | `__) 52 | 53 | Projects not using pythoncapi_compat.h 54 | ====================================== 55 | 56 | Projects not using ``pythoncapi_compat.h``: 57 | 58 | * numpy has its own compatibility layer, ``npy_pycompat.h`` and 59 | ``npy_3kcompat.h`` header files. It supports more C compilers than 60 | pythoncapi_compat.h: it supports ``__STRICT_ANSI__`` (ISO C90) for example. 61 | Rejected `PR 18713: MAINT: Use pythoncapi_compat.h in npy_3kcompat.h 62 | `_ (when it was rejected, numpy 63 | still had code for compatibility with Python 2.7). 64 | * Cython doesn't use pythoncapi_compat.h: 65 | `see Cython issue #3934 66 | `_. 67 | For example, `ModuleSetupCode.c 68 | `_ 69 | provides functions like ``__Pyx_SET_REFCNT()``. 70 | 71 | Project with a strict contributor agreement: 72 | 73 | * `zodbpickle 74 | `_ 75 | -------------------------------------------------------------------------------- /pythoncapi_compat.h: -------------------------------------------------------------------------------- 1 | // Header file providing new C API functions to old Python versions. 2 | // 3 | // File distributed under the Zero Clause BSD (0BSD) license. 4 | // Copyright Contributors to the pythoncapi_compat project. 5 | // 6 | // Homepage: 7 | // https://github.com/python/pythoncapi_compat 8 | // 9 | // Latest version: 10 | // https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h 11 | // 12 | // SPDX-License-Identifier: 0BSD 13 | 14 | #ifndef PYTHONCAPI_COMPAT 15 | #define PYTHONCAPI_COMPAT 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | #include // offsetof() 23 | 24 | // Python 3.11.0b4 added PyFrame_Back() to Python.h 25 | #if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) 26 | # include "frameobject.h" // PyFrameObject, PyFrame_GetBack() 27 | #endif 28 | #if PY_VERSION_HEX < 0x030C00A3 29 | # include // T_SHORT, READONLY 30 | #endif 31 | 32 | 33 | #ifndef _Py_CAST 34 | # define _Py_CAST(type, expr) ((type)(expr)) 35 | #endif 36 | 37 | // Static inline functions should use _Py_NULL rather than using directly NULL 38 | // to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, 39 | // _Py_NULL is defined as nullptr. 40 | #ifndef _Py_NULL 41 | # if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ 42 | || (defined(__cplusplus) && __cplusplus >= 201103) 43 | # define _Py_NULL nullptr 44 | # else 45 | # define _Py_NULL NULL 46 | # endif 47 | #endif 48 | 49 | // Cast argument to PyObject* type. 50 | #ifndef _PyObject_CAST 51 | # define _PyObject_CAST(op) _Py_CAST(PyObject*, op) 52 | #endif 53 | 54 | #ifndef Py_BUILD_ASSERT 55 | # define Py_BUILD_ASSERT(cond) \ 56 | do { \ 57 | (void)sizeof(char [1 - 2 * !(cond)]); \ 58 | } while(0) 59 | #endif 60 | 61 | 62 | // bpo-42262 added Py_NewRef() to Python 3.10.0a3 63 | #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) 64 | static inline PyObject* _Py_NewRef(PyObject *obj) 65 | { 66 | Py_INCREF(obj); 67 | return obj; 68 | } 69 | #define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) 70 | #endif 71 | 72 | 73 | // bpo-42262 added Py_XNewRef() to Python 3.10.0a3 74 | #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) 75 | static inline PyObject* _Py_XNewRef(PyObject *obj) 76 | { 77 | Py_XINCREF(obj); 78 | return obj; 79 | } 80 | #define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) 81 | #endif 82 | 83 | 84 | // bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 85 | #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) 86 | static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) 87 | { 88 | ob->ob_refcnt = refcnt; 89 | } 90 | #define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) 91 | #endif 92 | 93 | 94 | // Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. 95 | // It is excluded from the limited C API. 96 | #if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) 97 | #define Py_SETREF(dst, src) \ 98 | do { \ 99 | PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ 100 | PyObject *_tmp_dst = (*_tmp_dst_ptr); \ 101 | *_tmp_dst_ptr = _PyObject_CAST(src); \ 102 | Py_DECREF(_tmp_dst); \ 103 | } while (0) 104 | 105 | #define Py_XSETREF(dst, src) \ 106 | do { \ 107 | PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ 108 | PyObject *_tmp_dst = (*_tmp_dst_ptr); \ 109 | *_tmp_dst_ptr = _PyObject_CAST(src); \ 110 | Py_XDECREF(_tmp_dst); \ 111 | } while (0) 112 | #endif 113 | 114 | 115 | // bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() 116 | // to Python 3.10.0b1. 117 | #if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) 118 | # define Py_Is(x, y) ((x) == (y)) 119 | #endif 120 | #if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) 121 | # define Py_IsNone(x) Py_Is(x, Py_None) 122 | #endif 123 | #if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) 124 | # define Py_IsTrue(x) Py_Is(x, Py_True) 125 | #endif 126 | #if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) 127 | # define Py_IsFalse(x) Py_Is(x, Py_False) 128 | #endif 129 | 130 | 131 | // bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 132 | #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) 133 | static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) 134 | { 135 | ob->ob_type = type; 136 | } 137 | #define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) 138 | #endif 139 | 140 | 141 | // bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 142 | #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) 143 | static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) 144 | { 145 | ob->ob_size = size; 146 | } 147 | #define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) 148 | #endif 149 | 150 | 151 | // bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 152 | #if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) 153 | static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) 154 | { 155 | assert(frame != _Py_NULL); 156 | assert(frame->f_code != _Py_NULL); 157 | return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code)); 158 | } 159 | #endif 160 | 161 | static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) 162 | { 163 | PyCodeObject *code = PyFrame_GetCode(frame); 164 | Py_DECREF(code); 165 | return code; 166 | } 167 | 168 | 169 | // bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 170 | #if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) 171 | static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) 172 | { 173 | assert(frame != _Py_NULL); 174 | return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); 175 | } 176 | #endif 177 | 178 | #if !defined(PYPY_VERSION) 179 | static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) 180 | { 181 | PyFrameObject *back = PyFrame_GetBack(frame); 182 | Py_XDECREF(back); 183 | return back; 184 | } 185 | #endif 186 | 187 | 188 | // bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 189 | #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) 190 | static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) 191 | { 192 | #if PY_VERSION_HEX >= 0x030400B1 193 | if (PyFrame_FastToLocalsWithError(frame) < 0) { 194 | return NULL; 195 | } 196 | #else 197 | PyFrame_FastToLocals(frame); 198 | #endif 199 | return Py_NewRef(frame->f_locals); 200 | } 201 | #endif 202 | 203 | 204 | // bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 205 | #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) 206 | static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) 207 | { 208 | return Py_NewRef(frame->f_globals); 209 | } 210 | #endif 211 | 212 | 213 | // bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 214 | #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) 215 | static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) 216 | { 217 | return Py_NewRef(frame->f_builtins); 218 | } 219 | #endif 220 | 221 | 222 | // bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 223 | #if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) 224 | static inline int PyFrame_GetLasti(PyFrameObject *frame) 225 | { 226 | #if PY_VERSION_HEX >= 0x030A00A7 227 | // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, 228 | // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) 229 | // instructions. 230 | if (frame->f_lasti < 0) { 231 | return -1; 232 | } 233 | return frame->f_lasti * 2; 234 | #else 235 | return frame->f_lasti; 236 | #endif 237 | } 238 | #endif 239 | 240 | 241 | // gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 242 | #if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) 243 | static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) 244 | { 245 | PyObject *locals, *value; 246 | 247 | locals = PyFrame_GetLocals(frame); 248 | if (locals == NULL) { 249 | return NULL; 250 | } 251 | #if PY_VERSION_HEX >= 0x03000000 252 | value = PyDict_GetItemWithError(locals, name); 253 | #else 254 | value = _PyDict_GetItemWithError(locals, name); 255 | #endif 256 | Py_DECREF(locals); 257 | 258 | if (value == NULL) { 259 | if (PyErr_Occurred()) { 260 | return NULL; 261 | } 262 | #if PY_VERSION_HEX >= 0x03000000 263 | PyErr_Format(PyExc_NameError, "variable %R does not exist", name); 264 | #else 265 | PyErr_SetString(PyExc_NameError, "variable does not exist"); 266 | #endif 267 | return NULL; 268 | } 269 | return Py_NewRef(value); 270 | } 271 | #endif 272 | 273 | 274 | // gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 275 | #if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) 276 | static inline PyObject* 277 | PyFrame_GetVarString(PyFrameObject *frame, const char *name) 278 | { 279 | PyObject *name_obj, *value; 280 | #if PY_VERSION_HEX >= 0x03000000 281 | name_obj = PyUnicode_FromString(name); 282 | #else 283 | name_obj = PyString_FromString(name); 284 | #endif 285 | if (name_obj == NULL) { 286 | return NULL; 287 | } 288 | value = PyFrame_GetVar(frame, name_obj); 289 | Py_DECREF(name_obj); 290 | return value; 291 | } 292 | #endif 293 | 294 | 295 | // bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 296 | #if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) 297 | static inline PyInterpreterState * 298 | PyThreadState_GetInterpreter(PyThreadState *tstate) 299 | { 300 | assert(tstate != _Py_NULL); 301 | return tstate->interp; 302 | } 303 | #endif 304 | 305 | 306 | // bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 307 | #if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) 308 | static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) 309 | { 310 | assert(tstate != _Py_NULL); 311 | return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); 312 | } 313 | #endif 314 | 315 | #if !defined(PYPY_VERSION) 316 | static inline PyFrameObject* 317 | _PyThreadState_GetFrameBorrow(PyThreadState *tstate) 318 | { 319 | PyFrameObject *frame = PyThreadState_GetFrame(tstate); 320 | Py_XDECREF(frame); 321 | return frame; 322 | } 323 | #endif 324 | 325 | 326 | // bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 327 | #if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) 328 | static inline PyInterpreterState* PyInterpreterState_Get(void) 329 | { 330 | PyThreadState *tstate; 331 | PyInterpreterState *interp; 332 | 333 | tstate = PyThreadState_GET(); 334 | if (tstate == _Py_NULL) { 335 | Py_FatalError("GIL released (tstate is NULL)"); 336 | } 337 | interp = tstate->interp; 338 | if (interp == _Py_NULL) { 339 | Py_FatalError("no current interpreter"); 340 | } 341 | return interp; 342 | } 343 | #endif 344 | 345 | 346 | // bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 347 | #if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) 348 | static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) 349 | { 350 | assert(tstate != _Py_NULL); 351 | return tstate->id; 352 | } 353 | #endif 354 | 355 | // bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 356 | #if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) 357 | static inline void PyThreadState_EnterTracing(PyThreadState *tstate) 358 | { 359 | tstate->tracing++; 360 | #if PY_VERSION_HEX >= 0x030A00A1 361 | tstate->cframe->use_tracing = 0; 362 | #else 363 | tstate->use_tracing = 0; 364 | #endif 365 | } 366 | #endif 367 | 368 | // bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 369 | #if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) 370 | static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) 371 | { 372 | int use_tracing = (tstate->c_tracefunc != _Py_NULL 373 | || tstate->c_profilefunc != _Py_NULL); 374 | tstate->tracing--; 375 | #if PY_VERSION_HEX >= 0x030A00A1 376 | tstate->cframe->use_tracing = use_tracing; 377 | #else 378 | tstate->use_tracing = use_tracing; 379 | #endif 380 | } 381 | #endif 382 | 383 | 384 | // bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 385 | // PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 386 | #if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 387 | static inline PyObject* PyObject_CallNoArgs(PyObject *func) 388 | { 389 | return PyObject_CallFunctionObjArgs(func, NULL); 390 | } 391 | #endif 392 | 393 | 394 | // bpo-39245 made PyObject_CallOneArg() public (previously called 395 | // _PyObject_CallOneArg) in Python 3.9.0a4 396 | // PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 397 | #if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 398 | static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) 399 | { 400 | return PyObject_CallFunctionObjArgs(func, arg, NULL); 401 | } 402 | #endif 403 | 404 | 405 | // bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 406 | #if PY_VERSION_HEX < 0x030A00A3 407 | static inline int 408 | PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) 409 | { 410 | int res; 411 | 412 | if (!value && !PyErr_Occurred()) { 413 | // PyModule_AddObject() raises TypeError in this case 414 | PyErr_SetString(PyExc_SystemError, 415 | "PyModule_AddObjectRef() must be called " 416 | "with an exception raised if value is NULL"); 417 | return -1; 418 | } 419 | 420 | Py_XINCREF(value); 421 | res = PyModule_AddObject(module, name, value); 422 | if (res < 0) { 423 | Py_XDECREF(value); 424 | } 425 | return res; 426 | } 427 | #endif 428 | 429 | 430 | // bpo-40024 added PyModule_AddType() to Python 3.9.0a5 431 | #if PY_VERSION_HEX < 0x030900A5 432 | static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) 433 | { 434 | const char *name, *dot; 435 | 436 | if (PyType_Ready(type) < 0) { 437 | return -1; 438 | } 439 | 440 | // inline _PyType_Name() 441 | name = type->tp_name; 442 | assert(name != _Py_NULL); 443 | dot = strrchr(name, '.'); 444 | if (dot != _Py_NULL) { 445 | name = dot + 1; 446 | } 447 | 448 | return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); 449 | } 450 | #endif 451 | 452 | 453 | // bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. 454 | // bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. 455 | #if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) 456 | static inline int PyObject_GC_IsTracked(PyObject* obj) 457 | { 458 | return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); 459 | } 460 | #endif 461 | 462 | // bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. 463 | // bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. 464 | #if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) 465 | static inline int PyObject_GC_IsFinalized(PyObject *obj) 466 | { 467 | PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; 468 | return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); 469 | } 470 | #endif 471 | 472 | 473 | // bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 474 | #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) 475 | static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { 476 | return Py_TYPE(ob) == type; 477 | } 478 | #define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) 479 | #endif 480 | 481 | 482 | // bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. 483 | // bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. 484 | // Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal 485 | // C API: Python 3.11a2-3.11a6 versions are not supported. 486 | #if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) 487 | static inline int PyFloat_Pack2(double x, char *p, int le) 488 | { return _PyFloat_Pack2(x, (unsigned char*)p, le); } 489 | 490 | static inline double PyFloat_Unpack2(const char *p, int le) 491 | { return _PyFloat_Unpack2((const unsigned char *)p, le); } 492 | #endif 493 | 494 | 495 | // bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and 496 | // PyFloat_Unpack8() to Python 3.11a7. 497 | // Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() 498 | // and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions 499 | // are not supported. 500 | #if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) 501 | static inline int PyFloat_Pack4(double x, char *p, int le) 502 | { return _PyFloat_Pack4(x, (unsigned char*)p, le); } 503 | 504 | static inline int PyFloat_Pack8(double x, char *p, int le) 505 | { return _PyFloat_Pack8(x, (unsigned char*)p, le); } 506 | 507 | static inline double PyFloat_Unpack4(const char *p, int le) 508 | { return _PyFloat_Unpack4((const unsigned char *)p, le); } 509 | 510 | static inline double PyFloat_Unpack8(const char *p, int le) 511 | { return _PyFloat_Unpack8((const unsigned char *)p, le); } 512 | #endif 513 | 514 | 515 | // gh-92154 added PyCode_GetCode() to Python 3.11.0b1 516 | #if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) 517 | static inline PyObject* PyCode_GetCode(PyCodeObject *code) 518 | { 519 | return Py_NewRef(code->co_code); 520 | } 521 | #endif 522 | 523 | 524 | // gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 525 | #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) 526 | static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) 527 | { 528 | return Py_NewRef(code->co_varnames); 529 | } 530 | #endif 531 | 532 | // gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 533 | #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) 534 | static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) 535 | { 536 | return Py_NewRef(code->co_freevars); 537 | } 538 | #endif 539 | 540 | // gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 541 | #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) 542 | static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) 543 | { 544 | return Py_NewRef(code->co_cellvars); 545 | } 546 | #endif 547 | 548 | 549 | // Py_UNUSED() was added to Python 3.4.0b2. 550 | #if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) 551 | # if defined(__GNUC__) || defined(__clang__) 552 | # define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) 553 | # else 554 | # define Py_UNUSED(name) _unused_ ## name 555 | # endif 556 | #endif 557 | 558 | 559 | // gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 560 | #if PY_VERSION_HEX < 0x030D00A0 561 | static inline PyObject* PyImport_AddModuleRef(const char *name) 562 | { 563 | return Py_XNewRef(PyImport_AddModule(name)); 564 | } 565 | #endif 566 | 567 | 568 | // gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 569 | #if PY_VERSION_HEX < 0x030D0000 570 | static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) 571 | { 572 | PyObject *obj; 573 | if (ref != NULL && !PyWeakref_Check(ref)) { 574 | *pobj = NULL; 575 | PyErr_SetString(PyExc_TypeError, "expected a weakref"); 576 | return -1; 577 | } 578 | obj = PyWeakref_GetObject(ref); 579 | if (obj == NULL) { 580 | // SystemError if ref is NULL 581 | *pobj = NULL; 582 | return -1; 583 | } 584 | if (obj == Py_None) { 585 | *pobj = NULL; 586 | return 0; 587 | } 588 | *pobj = Py_NewRef(obj); 589 | return 1; 590 | } 591 | #endif 592 | 593 | 594 | // bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 595 | #ifndef PY_VECTORCALL_ARGUMENTS_OFFSET 596 | # define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) 597 | #endif 598 | 599 | // bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 600 | #if PY_VERSION_HEX < 0x030800B1 601 | static inline Py_ssize_t PyVectorcall_NARGS(size_t n) 602 | { 603 | return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; 604 | } 605 | #endif 606 | 607 | 608 | // gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 609 | #if PY_VERSION_HEX < 0x030900A4 610 | static inline PyObject* 611 | PyObject_Vectorcall(PyObject *callable, PyObject *const *args, 612 | size_t nargsf, PyObject *kwnames) 613 | { 614 | #if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) 615 | // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 616 | return _PyObject_Vectorcall(callable, args, nargsf, kwnames); 617 | #else 618 | PyObject *posargs = NULL, *kwargs = NULL; 619 | PyObject *res; 620 | Py_ssize_t nposargs, nkwargs, i; 621 | 622 | if (nargsf != 0 && args == NULL) { 623 | PyErr_BadInternalCall(); 624 | goto error; 625 | } 626 | if (kwnames != NULL && !PyTuple_Check(kwnames)) { 627 | PyErr_BadInternalCall(); 628 | goto error; 629 | } 630 | 631 | nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); 632 | if (kwnames) { 633 | nkwargs = PyTuple_GET_SIZE(kwnames); 634 | } 635 | else { 636 | nkwargs = 0; 637 | } 638 | 639 | posargs = PyTuple_New(nposargs); 640 | if (posargs == NULL) { 641 | goto error; 642 | } 643 | if (nposargs) { 644 | for (i=0; i < nposargs; i++) { 645 | PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); 646 | args++; 647 | } 648 | } 649 | 650 | if (nkwargs) { 651 | kwargs = PyDict_New(); 652 | if (kwargs == NULL) { 653 | goto error; 654 | } 655 | 656 | for (i = 0; i < nkwargs; i++) { 657 | PyObject *key = PyTuple_GET_ITEM(kwnames, i); 658 | PyObject *value = *args; 659 | args++; 660 | if (PyDict_SetItem(kwargs, key, value) < 0) { 661 | goto error; 662 | } 663 | } 664 | } 665 | else { 666 | kwargs = NULL; 667 | } 668 | 669 | res = PyObject_Call(callable, posargs, kwargs); 670 | Py_DECREF(posargs); 671 | Py_XDECREF(kwargs); 672 | return res; 673 | 674 | error: 675 | Py_DECREF(posargs); 676 | Py_XDECREF(kwargs); 677 | return NULL; 678 | #endif 679 | } 680 | #endif 681 | 682 | 683 | // gh-106521 added PyObject_GetOptionalAttr() and 684 | // PyObject_GetOptionalAttrString() to Python 3.13.0a1 685 | #if PY_VERSION_HEX < 0x030D00A1 686 | static inline int 687 | PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) 688 | { 689 | // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 690 | #if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) 691 | return _PyObject_LookupAttr(obj, attr_name, result); 692 | #else 693 | *result = PyObject_GetAttr(obj, attr_name); 694 | if (*result != NULL) { 695 | return 1; 696 | } 697 | if (!PyErr_Occurred()) { 698 | return 0; 699 | } 700 | if (PyErr_ExceptionMatches(PyExc_AttributeError)) { 701 | PyErr_Clear(); 702 | return 0; 703 | } 704 | return -1; 705 | #endif 706 | } 707 | 708 | static inline int 709 | PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) 710 | { 711 | PyObject *name_obj; 712 | int rc; 713 | #if PY_VERSION_HEX >= 0x03000000 714 | name_obj = PyUnicode_FromString(attr_name); 715 | #else 716 | name_obj = PyString_FromString(attr_name); 717 | #endif 718 | if (name_obj == NULL) { 719 | *result = NULL; 720 | return -1; 721 | } 722 | rc = PyObject_GetOptionalAttr(obj, name_obj, result); 723 | Py_DECREF(name_obj); 724 | return rc; 725 | } 726 | #endif 727 | 728 | 729 | // gh-106307 added PyObject_GetOptionalAttr() and 730 | // PyMapping_GetOptionalItemString() to Python 3.13.0a1 731 | #if PY_VERSION_HEX < 0x030D00A1 732 | static inline int 733 | PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) 734 | { 735 | *result = PyObject_GetItem(obj, key); 736 | if (*result) { 737 | return 1; 738 | } 739 | if (!PyErr_ExceptionMatches(PyExc_KeyError)) { 740 | return -1; 741 | } 742 | PyErr_Clear(); 743 | return 0; 744 | } 745 | 746 | static inline int 747 | PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) 748 | { 749 | PyObject *key_obj; 750 | int rc; 751 | #if PY_VERSION_HEX >= 0x03000000 752 | key_obj = PyUnicode_FromString(key); 753 | #else 754 | key_obj = PyString_FromString(key); 755 | #endif 756 | if (key_obj == NULL) { 757 | *result = NULL; 758 | return -1; 759 | } 760 | rc = PyMapping_GetOptionalItem(obj, key_obj, result); 761 | Py_DECREF(key_obj); 762 | return rc; 763 | } 764 | #endif 765 | 766 | // gh-108511 added PyMapping_HasKeyWithError() and 767 | // PyMapping_HasKeyStringWithError() to Python 3.13.0a1 768 | #if PY_VERSION_HEX < 0x030D00A1 769 | static inline int 770 | PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) 771 | { 772 | PyObject *res; 773 | int rc = PyMapping_GetOptionalItem(obj, key, &res); 774 | Py_XDECREF(res); 775 | return rc; 776 | } 777 | 778 | static inline int 779 | PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) 780 | { 781 | PyObject *res; 782 | int rc = PyMapping_GetOptionalItemString(obj, key, &res); 783 | Py_XDECREF(res); 784 | return rc; 785 | } 786 | #endif 787 | 788 | 789 | // gh-108511 added PyObject_HasAttrWithError() and 790 | // PyObject_HasAttrStringWithError() to Python 3.13.0a1 791 | #if PY_VERSION_HEX < 0x030D00A1 792 | static inline int 793 | PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) 794 | { 795 | PyObject *res; 796 | int rc = PyObject_GetOptionalAttr(obj, attr, &res); 797 | Py_XDECREF(res); 798 | return rc; 799 | } 800 | 801 | static inline int 802 | PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) 803 | { 804 | PyObject *res; 805 | int rc = PyObject_GetOptionalAttrString(obj, attr, &res); 806 | Py_XDECREF(res); 807 | return rc; 808 | } 809 | #endif 810 | 811 | 812 | // gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() 813 | // to Python 3.13.0a1 814 | #if PY_VERSION_HEX < 0x030D00A1 815 | static inline int 816 | PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) 817 | { 818 | #if PY_VERSION_HEX >= 0x03000000 819 | PyObject *item = PyDict_GetItemWithError(mp, key); 820 | #else 821 | PyObject *item = _PyDict_GetItemWithError(mp, key); 822 | #endif 823 | if (item != NULL) { 824 | *result = Py_NewRef(item); 825 | return 1; // found 826 | } 827 | if (!PyErr_Occurred()) { 828 | *result = NULL; 829 | return 0; // not found 830 | } 831 | *result = NULL; 832 | return -1; 833 | } 834 | 835 | static inline int 836 | PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) 837 | { 838 | int res; 839 | #if PY_VERSION_HEX >= 0x03000000 840 | PyObject *key_obj = PyUnicode_FromString(key); 841 | #else 842 | PyObject *key_obj = PyString_FromString(key); 843 | #endif 844 | if (key_obj == NULL) { 845 | *result = NULL; 846 | return -1; 847 | } 848 | res = PyDict_GetItemRef(mp, key_obj, result); 849 | Py_DECREF(key_obj); 850 | return res; 851 | } 852 | #endif 853 | 854 | 855 | // gh-106307 added PyModule_Add() to Python 3.13.0a1 856 | #if PY_VERSION_HEX < 0x030D00A1 857 | static inline int 858 | PyModule_Add(PyObject *mod, const char *name, PyObject *value) 859 | { 860 | int res = PyModule_AddObjectRef(mod, name, value); 861 | Py_XDECREF(value); 862 | return res; 863 | } 864 | #endif 865 | 866 | 867 | // gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 868 | // bpo-1856 added _Py_Finalizing to Python 3.2.1b1. 869 | // _Py_IsFinalizing() was added to PyPy 7.3.0. 870 | #if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \ 871 | && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) 872 | static inline int Py_IsFinalizing(void) 873 | { 874 | #if PY_VERSION_HEX >= 0x030700A1 875 | // _Py_IsFinalizing() was added to Python 3.7.0a1. 876 | return _Py_IsFinalizing(); 877 | #else 878 | return (_Py_Finalizing != NULL); 879 | #endif 880 | } 881 | #endif 882 | 883 | 884 | // gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 885 | #if PY_VERSION_HEX < 0x030D00A1 886 | static inline int PyDict_ContainsString(PyObject *op, const char *key) 887 | { 888 | PyObject *key_obj = PyUnicode_FromString(key); 889 | if (key_obj == NULL) { 890 | return -1; 891 | } 892 | int res = PyDict_Contains(op, key_obj); 893 | Py_DECREF(key_obj); 894 | return res; 895 | } 896 | #endif 897 | 898 | 899 | // gh-108445 added PyLong_AsInt() to Python 3.13.0a1 900 | #if PY_VERSION_HEX < 0x030D00A1 901 | static inline int PyLong_AsInt(PyObject *obj) 902 | { 903 | #ifdef PYPY_VERSION 904 | long value = PyLong_AsLong(obj); 905 | if (value == -1 && PyErr_Occurred()) { 906 | return -1; 907 | } 908 | if (value < (long)INT_MIN || (long)INT_MAX < value) { 909 | PyErr_SetString(PyExc_OverflowError, 910 | "Python int too large to convert to C int"); 911 | return -1; 912 | } 913 | return (int)value; 914 | #else 915 | return _PyLong_AsInt(obj); 916 | #endif 917 | } 918 | #endif 919 | 920 | 921 | // gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 922 | #if PY_VERSION_HEX < 0x030D00A1 923 | static inline int 924 | PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) 925 | { 926 | PyObject **dict = _PyObject_GetDictPtr(obj); 927 | if (dict == NULL || *dict == NULL) { 928 | return -1; 929 | } 930 | Py_VISIT(*dict); 931 | return 0; 932 | } 933 | 934 | static inline void 935 | PyObject_ClearManagedDict(PyObject *obj) 936 | { 937 | PyObject **dict = _PyObject_GetDictPtr(obj); 938 | if (dict == NULL || *dict == NULL) { 939 | return; 940 | } 941 | Py_CLEAR(*dict); 942 | } 943 | #endif 944 | 945 | // gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 946 | // Python 3.5.2 added _PyThreadState_UncheckedGet(). 947 | #if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 948 | static inline PyThreadState* 949 | PyThreadState_GetUnchecked(void) 950 | { 951 | return _PyThreadState_UncheckedGet(); 952 | } 953 | #endif 954 | 955 | // gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() 956 | // to Python 3.13.0a1 957 | #if PY_VERSION_HEX < 0x030D00A1 958 | static inline int 959 | PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) 960 | { 961 | Py_ssize_t len; 962 | const void *utf8; 963 | PyObject *exc_type, *exc_value, *exc_tb; 964 | int res; 965 | 966 | // API cannot report errors so save/restore the exception 967 | PyErr_Fetch(&exc_type, &exc_value, &exc_tb); 968 | 969 | // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() 970 | #if PY_VERSION_HEX >= 0x030300A1 971 | if (PyUnicode_IS_ASCII(unicode)) { 972 | utf8 = PyUnicode_DATA(unicode); 973 | len = PyUnicode_GET_LENGTH(unicode); 974 | } 975 | else { 976 | utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); 977 | if (utf8 == NULL) { 978 | // Memory allocation failure. The API cannot report error, 979 | // so ignore the exception and return 0. 980 | res = 0; 981 | goto done; 982 | } 983 | } 984 | 985 | if (len != str_len) { 986 | res = 0; 987 | goto done; 988 | } 989 | res = (memcmp(utf8, str, (size_t)len) == 0); 990 | #else 991 | PyObject *bytes = PyUnicode_AsUTF8String(unicode); 992 | if (bytes == NULL) { 993 | // Memory allocation failure. The API cannot report error, 994 | // so ignore the exception and return 0. 995 | res = 0; 996 | goto done; 997 | } 998 | 999 | #if PY_VERSION_HEX >= 0x03000000 1000 | len = PyBytes_GET_SIZE(bytes); 1001 | utf8 = PyBytes_AS_STRING(bytes); 1002 | #else 1003 | len = PyString_GET_SIZE(bytes); 1004 | utf8 = PyString_AS_STRING(bytes); 1005 | #endif 1006 | if (len != str_len) { 1007 | Py_DECREF(bytes); 1008 | res = 0; 1009 | goto done; 1010 | } 1011 | 1012 | res = (memcmp(utf8, str, (size_t)len) == 0); 1013 | Py_DECREF(bytes); 1014 | #endif 1015 | 1016 | done: 1017 | PyErr_Restore(exc_type, exc_value, exc_tb); 1018 | return res; 1019 | } 1020 | 1021 | static inline int 1022 | PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) 1023 | { 1024 | return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str)); 1025 | } 1026 | #endif 1027 | 1028 | 1029 | // gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 1030 | #if PY_VERSION_HEX < 0x030D00A2 1031 | static inline int 1032 | PyList_Extend(PyObject *list, PyObject *iterable) 1033 | { 1034 | return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable); 1035 | } 1036 | 1037 | static inline int 1038 | PyList_Clear(PyObject *list) 1039 | { 1040 | return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); 1041 | } 1042 | #endif 1043 | 1044 | // gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 1045 | #if PY_VERSION_HEX < 0x030D00A2 1046 | static inline int 1047 | PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) 1048 | { 1049 | PyObject *value; 1050 | 1051 | if (!PyDict_Check(dict)) { 1052 | PyErr_BadInternalCall(); 1053 | if (result) { 1054 | *result = NULL; 1055 | } 1056 | return -1; 1057 | } 1058 | 1059 | // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. 1060 | // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. 1061 | // Python 3.13.0a1 removed _PyDict_Pop(). 1062 | #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000 1063 | value = PyObject_CallMethod(dict, "pop", "O", key); 1064 | #elif PY_VERSION_HEX < 0x030600b3 1065 | value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL); 1066 | #else 1067 | value = _PyDict_Pop(dict, key, NULL); 1068 | #endif 1069 | if (value == NULL) { 1070 | if (result) { 1071 | *result = NULL; 1072 | } 1073 | if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) { 1074 | return -1; 1075 | } 1076 | PyErr_Clear(); 1077 | return 0; 1078 | } 1079 | if (result) { 1080 | *result = value; 1081 | } 1082 | else { 1083 | Py_DECREF(value); 1084 | } 1085 | return 1; 1086 | } 1087 | 1088 | static inline int 1089 | PyDict_PopString(PyObject *dict, const char *key, PyObject **result) 1090 | { 1091 | PyObject *key_obj = PyUnicode_FromString(key); 1092 | if (key_obj == NULL) { 1093 | if (result != NULL) { 1094 | *result = NULL; 1095 | } 1096 | return -1; 1097 | } 1098 | 1099 | int res = PyDict_Pop(dict, key_obj, result); 1100 | Py_DECREF(key_obj); 1101 | return res; 1102 | } 1103 | #endif 1104 | 1105 | 1106 | #if PY_VERSION_HEX < 0x030200A4 1107 | // Python 3.2.0a4 added Py_hash_t type 1108 | typedef Py_ssize_t Py_hash_t; 1109 | #endif 1110 | 1111 | 1112 | // gh-111545 added Py_HashPointer() to Python 3.13.0a3 1113 | #if PY_VERSION_HEX < 0x030D00A3 1114 | static inline Py_hash_t Py_HashPointer(const void *ptr) 1115 | { 1116 | #if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) 1117 | return _Py_HashPointer(ptr); 1118 | #else 1119 | return _Py_HashPointer(_Py_CAST(void*, ptr)); 1120 | #endif 1121 | } 1122 | #endif 1123 | 1124 | 1125 | // Python 3.13a4 added a PyTime API. 1126 | // Use the private API added to Python 3.5. 1127 | #if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 1128 | typedef _PyTime_t PyTime_t; 1129 | #define PyTime_MIN _PyTime_MIN 1130 | #define PyTime_MAX _PyTime_MAX 1131 | 1132 | static inline double PyTime_AsSecondsDouble(PyTime_t t) 1133 | { return _PyTime_AsSecondsDouble(t); } 1134 | 1135 | static inline int PyTime_Monotonic(PyTime_t *result) 1136 | { return _PyTime_GetMonotonicClockWithInfo(result, NULL); } 1137 | 1138 | static inline int PyTime_Time(PyTime_t *result) 1139 | { return _PyTime_GetSystemClockWithInfo(result, NULL); } 1140 | 1141 | static inline int PyTime_PerfCounter(PyTime_t *result) 1142 | { 1143 | #if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) 1144 | return _PyTime_GetPerfCounterWithInfo(result, NULL); 1145 | #elif PY_VERSION_HEX >= 0x03070000 1146 | // Call time.perf_counter_ns() and convert Python int object to PyTime_t. 1147 | // Cache time.perf_counter_ns() function for best performance. 1148 | static PyObject *func = NULL; 1149 | if (func == NULL) { 1150 | PyObject *mod = PyImport_ImportModule("time"); 1151 | if (mod == NULL) { 1152 | return -1; 1153 | } 1154 | 1155 | func = PyObject_GetAttrString(mod, "perf_counter_ns"); 1156 | Py_DECREF(mod); 1157 | if (func == NULL) { 1158 | return -1; 1159 | } 1160 | } 1161 | 1162 | PyObject *res = PyObject_CallNoArgs(func); 1163 | if (res == NULL) { 1164 | return -1; 1165 | } 1166 | long long value = PyLong_AsLongLong(res); 1167 | Py_DECREF(res); 1168 | 1169 | if (value == -1 && PyErr_Occurred()) { 1170 | return -1; 1171 | } 1172 | 1173 | Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t)); 1174 | *result = (PyTime_t)value; 1175 | return 0; 1176 | #else 1177 | // Call time.perf_counter() and convert C double to PyTime_t. 1178 | // Cache time.perf_counter() function for best performance. 1179 | static PyObject *func = NULL; 1180 | if (func == NULL) { 1181 | PyObject *mod = PyImport_ImportModule("time"); 1182 | if (mod == NULL) { 1183 | return -1; 1184 | } 1185 | 1186 | func = PyObject_GetAttrString(mod, "perf_counter"); 1187 | Py_DECREF(mod); 1188 | if (func == NULL) { 1189 | return -1; 1190 | } 1191 | } 1192 | 1193 | PyObject *res = PyObject_CallNoArgs(func); 1194 | if (res == NULL) { 1195 | return -1; 1196 | } 1197 | double d = PyFloat_AsDouble(res); 1198 | Py_DECREF(res); 1199 | 1200 | if (d == -1.0 && PyErr_Occurred()) { 1201 | return -1; 1202 | } 1203 | 1204 | // Avoid floor() to avoid having to link to libm 1205 | *result = (PyTime_t)(d * 1e9); 1206 | return 0; 1207 | #endif 1208 | } 1209 | 1210 | #endif 1211 | 1212 | // gh-111389 added hash constants to Python 3.13.0a5. These constants were 1213 | // added first as private macros to Python 3.4.0b1 and PyPy 7.3.8. 1214 | #if (!defined(PyHASH_BITS) \ 1215 | && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ 1216 | || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ 1217 | && PYPY_VERSION_NUM >= 0x07030800))) 1218 | # define PyHASH_BITS _PyHASH_BITS 1219 | # define PyHASH_MODULUS _PyHASH_MODULUS 1220 | # define PyHASH_INF _PyHASH_INF 1221 | # define PyHASH_IMAG _PyHASH_IMAG 1222 | #endif 1223 | 1224 | 1225 | // gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() 1226 | // to Python 3.13.0a6 1227 | #if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) 1228 | 1229 | #define Py_CONSTANT_NONE 0 1230 | #define Py_CONSTANT_FALSE 1 1231 | #define Py_CONSTANT_TRUE 2 1232 | #define Py_CONSTANT_ELLIPSIS 3 1233 | #define Py_CONSTANT_NOT_IMPLEMENTED 4 1234 | #define Py_CONSTANT_ZERO 5 1235 | #define Py_CONSTANT_ONE 6 1236 | #define Py_CONSTANT_EMPTY_STR 7 1237 | #define Py_CONSTANT_EMPTY_BYTES 8 1238 | #define Py_CONSTANT_EMPTY_TUPLE 9 1239 | 1240 | static inline PyObject* Py_GetConstant(unsigned int constant_id) 1241 | { 1242 | static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; 1243 | 1244 | if (constants[Py_CONSTANT_NONE] == NULL) { 1245 | constants[Py_CONSTANT_NONE] = Py_None; 1246 | constants[Py_CONSTANT_FALSE] = Py_False; 1247 | constants[Py_CONSTANT_TRUE] = Py_True; 1248 | constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis; 1249 | constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented; 1250 | 1251 | constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0); 1252 | if (constants[Py_CONSTANT_ZERO] == NULL) { 1253 | goto fatal_error; 1254 | } 1255 | 1256 | constants[Py_CONSTANT_ONE] = PyLong_FromLong(1); 1257 | if (constants[Py_CONSTANT_ONE] == NULL) { 1258 | goto fatal_error; 1259 | } 1260 | 1261 | constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0); 1262 | if (constants[Py_CONSTANT_EMPTY_STR] == NULL) { 1263 | goto fatal_error; 1264 | } 1265 | 1266 | constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0); 1267 | if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) { 1268 | goto fatal_error; 1269 | } 1270 | 1271 | constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0); 1272 | if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) { 1273 | goto fatal_error; 1274 | } 1275 | // goto dance to avoid compiler warnings about Py_FatalError() 1276 | goto init_done; 1277 | 1278 | fatal_error: 1279 | // This case should never happen 1280 | Py_FatalError("Py_GetConstant() failed to get constants"); 1281 | } 1282 | 1283 | init_done: 1284 | if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { 1285 | return Py_NewRef(constants[constant_id]); 1286 | } 1287 | else { 1288 | PyErr_BadInternalCall(); 1289 | return NULL; 1290 | } 1291 | } 1292 | 1293 | static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) 1294 | { 1295 | PyObject *obj = Py_GetConstant(constant_id); 1296 | Py_XDECREF(obj); 1297 | return obj; 1298 | } 1299 | #endif 1300 | 1301 | 1302 | // gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 1303 | #if PY_VERSION_HEX < 0x030D00A4 1304 | static inline PyObject * 1305 | PyList_GetItemRef(PyObject *op, Py_ssize_t index) 1306 | { 1307 | PyObject *item = PyList_GetItem(op, index); 1308 | Py_XINCREF(item); 1309 | return item; 1310 | } 1311 | #endif 1312 | 1313 | 1314 | // gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 1315 | #if PY_VERSION_HEX < 0x030D00A4 1316 | static inline int 1317 | PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, 1318 | PyObject **result) 1319 | { 1320 | PyObject *value; 1321 | if (PyDict_GetItemRef(d, key, &value) < 0) { 1322 | // get error 1323 | if (result) { 1324 | *result = NULL; 1325 | } 1326 | return -1; 1327 | } 1328 | if (value != NULL) { 1329 | // present 1330 | if (result) { 1331 | *result = value; 1332 | } 1333 | else { 1334 | Py_DECREF(value); 1335 | } 1336 | return 1; 1337 | } 1338 | 1339 | // missing: set the item 1340 | if (PyDict_SetItem(d, key, default_value) < 0) { 1341 | // set error 1342 | if (result) { 1343 | *result = NULL; 1344 | } 1345 | return -1; 1346 | } 1347 | if (result) { 1348 | *result = Py_NewRef(default_value); 1349 | } 1350 | return 0; 1351 | } 1352 | #endif 1353 | 1354 | #if PY_VERSION_HEX < 0x030D00B3 1355 | # define Py_BEGIN_CRITICAL_SECTION(op) { 1356 | # define Py_END_CRITICAL_SECTION() } 1357 | # define Py_BEGIN_CRITICAL_SECTION2(a, b) { 1358 | # define Py_END_CRITICAL_SECTION2() } 1359 | #endif 1360 | 1361 | #if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) 1362 | typedef struct PyUnicodeWriter PyUnicodeWriter; 1363 | 1364 | static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) 1365 | { 1366 | _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); 1367 | PyMem_Free(writer); 1368 | } 1369 | 1370 | static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) 1371 | { 1372 | if (length < 0) { 1373 | PyErr_SetString(PyExc_ValueError, 1374 | "length must be positive"); 1375 | return NULL; 1376 | } 1377 | 1378 | const size_t size = sizeof(_PyUnicodeWriter); 1379 | PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); 1380 | if (pub_writer == _Py_NULL) { 1381 | PyErr_NoMemory(); 1382 | return _Py_NULL; 1383 | } 1384 | _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; 1385 | 1386 | _PyUnicodeWriter_Init(writer); 1387 | if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { 1388 | PyUnicodeWriter_Discard(pub_writer); 1389 | return NULL; 1390 | } 1391 | writer->overallocate = 1; 1392 | return pub_writer; 1393 | } 1394 | 1395 | static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) 1396 | { 1397 | PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); 1398 | assert(((_PyUnicodeWriter*)writer)->buffer == NULL); 1399 | PyMem_Free(writer); 1400 | return str; 1401 | } 1402 | 1403 | static inline int 1404 | PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) 1405 | { 1406 | if (ch > 0x10ffff) { 1407 | PyErr_SetString(PyExc_ValueError, 1408 | "character must be in range(0x110000)"); 1409 | return -1; 1410 | } 1411 | 1412 | return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); 1413 | } 1414 | 1415 | static inline int 1416 | PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) 1417 | { 1418 | PyObject *str = PyObject_Str(obj); 1419 | if (str == NULL) { 1420 | return -1; 1421 | } 1422 | 1423 | int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); 1424 | Py_DECREF(str); 1425 | return res; 1426 | } 1427 | 1428 | static inline int 1429 | PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) 1430 | { 1431 | PyObject *str = PyObject_Repr(obj); 1432 | if (str == NULL) { 1433 | return -1; 1434 | } 1435 | 1436 | int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); 1437 | Py_DECREF(str); 1438 | return res; 1439 | } 1440 | 1441 | static inline int 1442 | PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, 1443 | const char *str, Py_ssize_t size) 1444 | { 1445 | if (size < 0) { 1446 | size = (Py_ssize_t)strlen(str); 1447 | } 1448 | 1449 | PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); 1450 | if (str_obj == _Py_NULL) { 1451 | return -1; 1452 | } 1453 | 1454 | int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); 1455 | Py_DECREF(str_obj); 1456 | return res; 1457 | } 1458 | 1459 | static inline int 1460 | PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, 1461 | const wchar_t *str, Py_ssize_t size) 1462 | { 1463 | if (size < 0) { 1464 | size = (Py_ssize_t)wcslen(str); 1465 | } 1466 | 1467 | PyObject *str_obj = PyUnicode_FromWideChar(str, size); 1468 | if (str_obj == _Py_NULL) { 1469 | return -1; 1470 | } 1471 | 1472 | int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); 1473 | Py_DECREF(str_obj); 1474 | return res; 1475 | } 1476 | 1477 | static inline int 1478 | PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, 1479 | Py_ssize_t start, Py_ssize_t end) 1480 | { 1481 | if (!PyUnicode_Check(str)) { 1482 | PyErr_Format(PyExc_TypeError, "expect str, not %T", str); 1483 | return -1; 1484 | } 1485 | if (start < 0 || start > end) { 1486 | PyErr_Format(PyExc_ValueError, "invalid start argument"); 1487 | return -1; 1488 | } 1489 | if (end > PyUnicode_GET_LENGTH(str)) { 1490 | PyErr_Format(PyExc_ValueError, "invalid end argument"); 1491 | return -1; 1492 | } 1493 | 1494 | return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, 1495 | start, end); 1496 | } 1497 | 1498 | static inline int 1499 | PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) 1500 | { 1501 | va_list vargs; 1502 | va_start(vargs, format); 1503 | PyObject *str = PyUnicode_FromFormatV(format, vargs); 1504 | va_end(vargs); 1505 | if (str == _Py_NULL) { 1506 | return -1; 1507 | } 1508 | 1509 | int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); 1510 | Py_DECREF(str); 1511 | return res; 1512 | } 1513 | #endif // PY_VERSION_HEX < 0x030E0000 1514 | 1515 | // gh-116560 added PyLong_GetSign() to Python 3.14.0a0 1516 | #if PY_VERSION_HEX < 0x030E00A0 1517 | static inline int PyLong_GetSign(PyObject *obj, int *sign) 1518 | { 1519 | if (!PyLong_Check(obj)) { 1520 | PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); 1521 | return -1; 1522 | } 1523 | 1524 | *sign = _PyLong_Sign(obj); 1525 | return 0; 1526 | } 1527 | #endif 1528 | 1529 | // gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2 1530 | #if PY_VERSION_HEX < 0x030E00A2 1531 | static inline int PyLong_IsPositive(PyObject *obj) 1532 | { 1533 | if (!PyLong_Check(obj)) { 1534 | PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); 1535 | return -1; 1536 | } 1537 | return _PyLong_Sign(obj) == 1; 1538 | } 1539 | 1540 | static inline int PyLong_IsNegative(PyObject *obj) 1541 | { 1542 | if (!PyLong_Check(obj)) { 1543 | PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); 1544 | return -1; 1545 | } 1546 | return _PyLong_Sign(obj) == -1; 1547 | } 1548 | 1549 | static inline int PyLong_IsZero(PyObject *obj) 1550 | { 1551 | if (!PyLong_Check(obj)) { 1552 | PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); 1553 | return -1; 1554 | } 1555 | return _PyLong_Sign(obj) == 0; 1556 | } 1557 | #endif 1558 | 1559 | 1560 | // gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 1561 | #if PY_VERSION_HEX < 0x030E00A0 1562 | static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) 1563 | { 1564 | if (!PyUnicode_Check(str1)) { 1565 | PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", 1566 | Py_TYPE(str1)->tp_name); 1567 | return -1; 1568 | } 1569 | if (!PyUnicode_Check(str2)) { 1570 | PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", 1571 | Py_TYPE(str2)->tp_name); 1572 | return -1; 1573 | } 1574 | 1575 | #if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) 1576 | PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); 1577 | 1578 | return _PyUnicode_Equal(str1, str2); 1579 | #elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) 1580 | return _PyUnicode_EQ(str1, str2); 1581 | #elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) 1582 | return _PyUnicode_EQ(str1, str2); 1583 | #else 1584 | return (PyUnicode_Compare(str1, str2) == 0); 1585 | #endif 1586 | } 1587 | #endif 1588 | 1589 | 1590 | // gh-121645 added PyBytes_Join() to Python 3.14.0a0 1591 | #if PY_VERSION_HEX < 0x030E00A0 1592 | static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) 1593 | { 1594 | return _PyBytes_Join(sep, iterable); 1595 | } 1596 | #endif 1597 | 1598 | 1599 | #if PY_VERSION_HEX < 0x030E00A0 1600 | static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) 1601 | { 1602 | #if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) 1603 | PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); 1604 | 1605 | return _Py_HashBytes(ptr, len); 1606 | #else 1607 | Py_hash_t hash; 1608 | PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len); 1609 | if (bytes == NULL) { 1610 | return -1; 1611 | } 1612 | hash = PyObject_Hash(bytes); 1613 | Py_DECREF(bytes); 1614 | return hash; 1615 | #endif 1616 | } 1617 | #endif 1618 | 1619 | 1620 | #if PY_VERSION_HEX < 0x030E00A0 1621 | static inline int PyIter_NextItem(PyObject *iter, PyObject **item) 1622 | { 1623 | iternextfunc tp_iternext; 1624 | 1625 | assert(iter != NULL); 1626 | assert(item != NULL); 1627 | 1628 | tp_iternext = Py_TYPE(iter)->tp_iternext; 1629 | if (tp_iternext == NULL) { 1630 | *item = NULL; 1631 | PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", 1632 | Py_TYPE(iter)->tp_name); 1633 | return -1; 1634 | } 1635 | 1636 | if ((*item = tp_iternext(iter))) { 1637 | return 1; 1638 | } 1639 | if (!PyErr_Occurred()) { 1640 | return 0; 1641 | } 1642 | if (PyErr_ExceptionMatches(PyExc_StopIteration)) { 1643 | PyErr_Clear(); 1644 | return 0; 1645 | } 1646 | return -1; 1647 | } 1648 | #endif 1649 | 1650 | 1651 | #if PY_VERSION_HEX < 0x030E00A0 1652 | static inline PyObject* PyLong_FromInt32(int32_t value) 1653 | { 1654 | Py_BUILD_ASSERT(sizeof(long) >= 4); 1655 | return PyLong_FromLong(value); 1656 | } 1657 | 1658 | static inline PyObject* PyLong_FromInt64(int64_t value) 1659 | { 1660 | Py_BUILD_ASSERT(sizeof(long long) >= 8); 1661 | return PyLong_FromLongLong(value); 1662 | } 1663 | 1664 | static inline PyObject* PyLong_FromUInt32(uint32_t value) 1665 | { 1666 | Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); 1667 | return PyLong_FromUnsignedLong(value); 1668 | } 1669 | 1670 | static inline PyObject* PyLong_FromUInt64(uint64_t value) 1671 | { 1672 | Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); 1673 | return PyLong_FromUnsignedLongLong(value); 1674 | } 1675 | 1676 | static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) 1677 | { 1678 | Py_BUILD_ASSERT(sizeof(int) == 4); 1679 | int value = PyLong_AsInt(obj); 1680 | if (value == -1 && PyErr_Occurred()) { 1681 | return -1; 1682 | } 1683 | *pvalue = (int32_t)value; 1684 | return 0; 1685 | } 1686 | 1687 | static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) 1688 | { 1689 | Py_BUILD_ASSERT(sizeof(long long) == 8); 1690 | long long value = PyLong_AsLongLong(obj); 1691 | if (value == -1 && PyErr_Occurred()) { 1692 | return -1; 1693 | } 1694 | *pvalue = (int64_t)value; 1695 | return 0; 1696 | } 1697 | 1698 | static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) 1699 | { 1700 | Py_BUILD_ASSERT(sizeof(long) >= 4); 1701 | unsigned long value = PyLong_AsUnsignedLong(obj); 1702 | if (value == (unsigned long)-1 && PyErr_Occurred()) { 1703 | return -1; 1704 | } 1705 | #if SIZEOF_LONG > 4 1706 | if ((unsigned long)UINT32_MAX < value) { 1707 | PyErr_SetString(PyExc_OverflowError, 1708 | "Python int too large to convert to C uint32_t"); 1709 | return -1; 1710 | } 1711 | #endif 1712 | *pvalue = (uint32_t)value; 1713 | return 0; 1714 | } 1715 | 1716 | static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) 1717 | { 1718 | Py_BUILD_ASSERT(sizeof(long long) == 8); 1719 | unsigned long long value = PyLong_AsUnsignedLongLong(obj); 1720 | if (value == (unsigned long long)-1 && PyErr_Occurred()) { 1721 | return -1; 1722 | } 1723 | *pvalue = (uint64_t)value; 1724 | return 0; 1725 | } 1726 | #endif 1727 | 1728 | 1729 | // gh-102471 added import and export API for integers to 3.14.0a2. 1730 | #if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) 1731 | // Helpers to access PyLongObject internals. 1732 | static inline void 1733 | _PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) 1734 | { 1735 | #if PY_VERSION_HEX >= 0x030C0000 1736 | op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3); 1737 | #elif PY_VERSION_HEX >= 0x030900A4 1738 | Py_SET_SIZE(op, sign * size); 1739 | #else 1740 | Py_SIZE(op) = sign * size; 1741 | #endif 1742 | } 1743 | 1744 | static inline Py_ssize_t 1745 | _PyLong_DigitCount(const PyLongObject *op) 1746 | { 1747 | #if PY_VERSION_HEX >= 0x030C0000 1748 | return (Py_ssize_t)(op->long_value.lv_tag >> 3); 1749 | #else 1750 | return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); 1751 | #endif 1752 | } 1753 | 1754 | static inline digit* 1755 | _PyLong_GetDigits(const PyLongObject *op) 1756 | { 1757 | #if PY_VERSION_HEX >= 0x030C0000 1758 | return (digit*)(op->long_value.ob_digit); 1759 | #else 1760 | return (digit*)(op->ob_digit); 1761 | #endif 1762 | } 1763 | 1764 | typedef struct PyLongLayout { 1765 | uint8_t bits_per_digit; 1766 | uint8_t digit_size; 1767 | int8_t digits_order; 1768 | int8_t digit_endianness; 1769 | } PyLongLayout; 1770 | 1771 | typedef struct PyLongExport { 1772 | int64_t value; 1773 | uint8_t negative; 1774 | Py_ssize_t ndigits; 1775 | const void *digits; 1776 | Py_uintptr_t _reserved; 1777 | } PyLongExport; 1778 | 1779 | typedef struct PyLongWriter PyLongWriter; 1780 | 1781 | static inline const PyLongLayout* 1782 | PyLong_GetNativeLayout(void) 1783 | { 1784 | static const PyLongLayout PyLong_LAYOUT = { 1785 | PyLong_SHIFT, 1786 | sizeof(digit), 1787 | -1, // least significant first 1788 | PY_LITTLE_ENDIAN ? -1 : 1, 1789 | }; 1790 | 1791 | return &PyLong_LAYOUT; 1792 | } 1793 | 1794 | static inline int 1795 | PyLong_Export(PyObject *obj, PyLongExport *export_long) 1796 | { 1797 | if (!PyLong_Check(obj)) { 1798 | memset(export_long, 0, sizeof(*export_long)); 1799 | PyErr_Format(PyExc_TypeError, "expected int, got %s", 1800 | Py_TYPE(obj)->tp_name); 1801 | return -1; 1802 | } 1803 | 1804 | // Fast-path: try to convert to a int64_t 1805 | PyLongObject *self = (PyLongObject*)obj; 1806 | int overflow; 1807 | #if SIZEOF_LONG == 8 1808 | long value = PyLong_AsLongAndOverflow(obj, &overflow); 1809 | #else 1810 | // Windows has 32-bit long, so use 64-bit long long instead 1811 | long long value = PyLong_AsLongLongAndOverflow(obj, &overflow); 1812 | #endif 1813 | Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t)); 1814 | // the function cannot fail since obj is a PyLongObject 1815 | assert(!(value == -1 && PyErr_Occurred())); 1816 | 1817 | if (!overflow) { 1818 | export_long->value = value; 1819 | export_long->negative = 0; 1820 | export_long->ndigits = 0; 1821 | export_long->digits = 0; 1822 | export_long->_reserved = 0; 1823 | } 1824 | else { 1825 | export_long->value = 0; 1826 | export_long->negative = _PyLong_Sign(obj) < 0; 1827 | export_long->ndigits = _PyLong_DigitCount(self); 1828 | if (export_long->ndigits == 0) { 1829 | export_long->ndigits = 1; 1830 | } 1831 | export_long->digits = _PyLong_GetDigits(self); 1832 | export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj); 1833 | } 1834 | return 0; 1835 | } 1836 | 1837 | static inline void 1838 | PyLong_FreeExport(PyLongExport *export_long) 1839 | { 1840 | PyObject *obj = (PyObject*)export_long->_reserved; 1841 | 1842 | if (obj) { 1843 | export_long->_reserved = 0; 1844 | Py_DECREF(obj); 1845 | } 1846 | } 1847 | 1848 | static inline PyLongWriter* 1849 | PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) 1850 | { 1851 | if (ndigits <= 0) { 1852 | PyErr_SetString(PyExc_ValueError, "ndigits must be positive"); 1853 | return NULL; 1854 | } 1855 | assert(digits != NULL); 1856 | 1857 | PyLongObject *obj = _PyLong_New(ndigits); 1858 | if (obj == NULL) { 1859 | return NULL; 1860 | } 1861 | _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits); 1862 | 1863 | *digits = _PyLong_GetDigits(obj); 1864 | return (PyLongWriter*)obj; 1865 | } 1866 | 1867 | static inline void 1868 | PyLongWriter_Discard(PyLongWriter *writer) 1869 | { 1870 | PyLongObject *obj = (PyLongObject *)writer; 1871 | 1872 | assert(Py_REFCNT(obj) == 1); 1873 | Py_DECREF(obj); 1874 | } 1875 | 1876 | static inline PyObject* 1877 | PyLongWriter_Finish(PyLongWriter *writer) 1878 | { 1879 | PyObject *obj = (PyObject *)writer; 1880 | PyLongObject *self = (PyLongObject*)obj; 1881 | Py_ssize_t j = _PyLong_DigitCount(self); 1882 | Py_ssize_t i = j; 1883 | int sign = _PyLong_Sign(obj); 1884 | 1885 | assert(Py_REFCNT(obj) == 1); 1886 | 1887 | // Normalize and get singleton if possible 1888 | while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) { 1889 | --i; 1890 | } 1891 | if (i != j) { 1892 | if (i == 0) { 1893 | sign = 0; 1894 | } 1895 | _PyLong_SetSignAndDigitCount(self, sign, i); 1896 | } 1897 | if (i <= 1) { 1898 | long val = sign * (long)(_PyLong_GetDigits(self)[0]); 1899 | Py_DECREF(obj); 1900 | return PyLong_FromLong(val); 1901 | } 1902 | 1903 | return obj; 1904 | } 1905 | #endif 1906 | 1907 | 1908 | #if PY_VERSION_HEX < 0x030C00A3 1909 | # define Py_T_SHORT T_SHORT 1910 | # define Py_T_INT T_INT 1911 | # define Py_T_LONG T_LONG 1912 | # define Py_T_FLOAT T_FLOAT 1913 | # define Py_T_DOUBLE T_DOUBLE 1914 | # define Py_T_STRING T_STRING 1915 | # define _Py_T_OBJECT T_OBJECT 1916 | # define Py_T_CHAR T_CHAR 1917 | # define Py_T_BYTE T_BYTE 1918 | # define Py_T_UBYTE T_UBYTE 1919 | # define Py_T_USHORT T_USHORT 1920 | # define Py_T_UINT T_UINT 1921 | # define Py_T_ULONG T_ULONG 1922 | # define Py_T_STRING_INPLACE T_STRING_INPLACE 1923 | # define Py_T_BOOL T_BOOL 1924 | # define Py_T_OBJECT_EX T_OBJECT_EX 1925 | # define Py_T_LONGLONG T_LONGLONG 1926 | # define Py_T_ULONGLONG T_ULONGLONG 1927 | # define Py_T_PYSSIZET T_PYSSIZET 1928 | 1929 | # if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) 1930 | # define _Py_T_NONE T_NONE 1931 | # endif 1932 | 1933 | # define Py_READONLY READONLY 1934 | # define Py_AUDIT_READ READ_RESTRICTED 1935 | # define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED 1936 | #endif 1937 | 1938 | 1939 | // gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4 1940 | #if PY_VERSION_HEX < 0x030E00A4 1941 | static inline FILE* Py_fopen(PyObject *path, const char *mode) 1942 | { 1943 | #if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION) 1944 | PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode); 1945 | 1946 | return _Py_fopen_obj(path, mode); 1947 | #else 1948 | FILE *f; 1949 | PyObject *bytes; 1950 | #if PY_VERSION_HEX >= 0x03000000 1951 | if (!PyUnicode_FSConverter(path, &bytes)) { 1952 | return NULL; 1953 | } 1954 | #else 1955 | if (!PyString_Check(path)) { 1956 | PyErr_SetString(PyExc_TypeError, "except str"); 1957 | return NULL; 1958 | } 1959 | bytes = Py_NewRef(path); 1960 | #endif 1961 | const char *path_bytes = PyBytes_AS_STRING(bytes); 1962 | 1963 | f = fopen(path_bytes, mode); 1964 | Py_DECREF(bytes); 1965 | 1966 | if (f == NULL) { 1967 | PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); 1968 | return NULL; 1969 | } 1970 | return f; 1971 | #endif 1972 | } 1973 | 1974 | static inline int Py_fclose(FILE *file) 1975 | { 1976 | return fclose(file); 1977 | } 1978 | #endif 1979 | 1980 | 1981 | #if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) 1982 | static inline PyObject* 1983 | PyConfig_Get(const char *name) 1984 | { 1985 | typedef enum { 1986 | _PyConfig_MEMBER_INT, 1987 | _PyConfig_MEMBER_UINT, 1988 | _PyConfig_MEMBER_ULONG, 1989 | _PyConfig_MEMBER_BOOL, 1990 | _PyConfig_MEMBER_WSTR, 1991 | _PyConfig_MEMBER_WSTR_OPT, 1992 | _PyConfig_MEMBER_WSTR_LIST, 1993 | } PyConfigMemberType; 1994 | 1995 | typedef struct { 1996 | const char *name; 1997 | size_t offset; 1998 | PyConfigMemberType type; 1999 | const char *sys_attr; 2000 | } PyConfigSpec; 2001 | 2002 | #define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ 2003 | {#MEMBER, offsetof(PyConfig, MEMBER), \ 2004 | _PyConfig_MEMBER_##TYPE, sys_attr} 2005 | 2006 | static const PyConfigSpec config_spec[] = { 2007 | PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"), 2008 | PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"), 2009 | PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"), 2010 | PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"), 2011 | PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL), 2012 | PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"), 2013 | PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"), 2014 | PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL), 2015 | #if 0x030C0000 <= PY_VERSION_HEX 2016 | PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL), 2017 | #endif 2018 | PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL), 2019 | PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"), 2020 | PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL), 2021 | PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL), 2022 | PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"), 2023 | PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"), 2024 | PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"), 2025 | PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL), 2026 | #if 0x030B0000 <= PY_VERSION_HEX 2027 | PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"), 2028 | #endif 2029 | PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL), 2030 | PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL), 2031 | PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"), 2032 | PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL), 2033 | PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), 2034 | PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), 2035 | PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), 2036 | #if 0x030B0000 <= PY_VERSION_HEX 2037 | PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL), 2038 | #endif 2039 | PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL), 2040 | #if 0x030D0000 <= PY_VERSION_HEX 2041 | PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL), 2042 | #endif 2043 | PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL), 2044 | PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL), 2045 | #if 0x030B0000 <= PY_VERSION_HEX 2046 | PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL), 2047 | #endif 2048 | #ifdef Py_GIL_DISABLED 2049 | PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL), 2050 | #endif 2051 | PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL), 2052 | PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL), 2053 | PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL), 2054 | PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL), 2055 | PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL), 2056 | PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL), 2057 | PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL), 2058 | PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL), 2059 | #ifdef MS_WINDOWS 2060 | PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL), 2061 | #endif 2062 | PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL), 2063 | #if 0x030A0000 <= PY_VERSION_HEX 2064 | PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"), 2065 | #endif 2066 | PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL), 2067 | PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL), 2068 | #if 0x030C0000 <= PY_VERSION_HEX 2069 | PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL), 2070 | #endif 2071 | PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL), 2072 | PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), 2073 | PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), 2074 | PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), 2075 | #if 0x030B0000 <= PY_VERSION_HEX 2076 | PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), 2077 | #endif 2078 | PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL), 2079 | PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL), 2080 | PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL), 2081 | PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL), 2082 | PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL), 2083 | PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL), 2084 | #if 0x030B0000 <= PY_VERSION_HEX 2085 | PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), 2086 | #endif 2087 | PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), 2088 | PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), 2089 | #if 0x030A0000 <= PY_VERSION_HEX 2090 | PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL), 2091 | #endif 2092 | }; 2093 | 2094 | #undef PYTHONCAPI_COMPAT_SPEC 2095 | 2096 | const PyConfigSpec *spec; 2097 | int found = 0; 2098 | for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) { 2099 | spec = &config_spec[i]; 2100 | if (strcmp(spec->name, name) == 0) { 2101 | found = 1; 2102 | break; 2103 | } 2104 | } 2105 | if (found) { 2106 | if (spec->sys_attr != NULL) { 2107 | PyObject *value = PySys_GetObject(spec->sys_attr); 2108 | if (value == NULL) { 2109 | PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); 2110 | return NULL; 2111 | } 2112 | return Py_NewRef(value); 2113 | } 2114 | 2115 | PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); 2116 | 2117 | const PyConfig *config = _Py_GetConfig(); 2118 | void *member = (char *)config + spec->offset; 2119 | switch (spec->type) { 2120 | case _PyConfig_MEMBER_INT: 2121 | case _PyConfig_MEMBER_UINT: 2122 | { 2123 | int value = *(int *)member; 2124 | return PyLong_FromLong(value); 2125 | } 2126 | case _PyConfig_MEMBER_BOOL: 2127 | { 2128 | int value = *(int *)member; 2129 | return PyBool_FromLong(value != 0); 2130 | } 2131 | case _PyConfig_MEMBER_ULONG: 2132 | { 2133 | unsigned long value = *(unsigned long *)member; 2134 | return PyLong_FromUnsignedLong(value); 2135 | } 2136 | case _PyConfig_MEMBER_WSTR: 2137 | case _PyConfig_MEMBER_WSTR_OPT: 2138 | { 2139 | wchar_t *wstr = *(wchar_t **)member; 2140 | if (wstr != NULL) { 2141 | return PyUnicode_FromWideChar(wstr, -1); 2142 | } 2143 | else { 2144 | return Py_NewRef(Py_None); 2145 | } 2146 | } 2147 | case _PyConfig_MEMBER_WSTR_LIST: 2148 | { 2149 | const PyWideStringList *list = (const PyWideStringList *)member; 2150 | PyObject *tuple = PyTuple_New(list->length); 2151 | if (tuple == NULL) { 2152 | return NULL; 2153 | } 2154 | 2155 | for (Py_ssize_t i = 0; i < list->length; i++) { 2156 | PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); 2157 | if (item == NULL) { 2158 | Py_DECREF(tuple); 2159 | return NULL; 2160 | } 2161 | PyTuple_SET_ITEM(tuple, i, item); 2162 | } 2163 | return tuple; 2164 | } 2165 | default: 2166 | Py_UNREACHABLE(); 2167 | } 2168 | } 2169 | 2170 | PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name); 2171 | return NULL; 2172 | } 2173 | 2174 | static inline int 2175 | PyConfig_GetInt(const char *name, int *value) 2176 | { 2177 | PyObject *obj = PyConfig_Get(name); 2178 | if (obj == NULL) { 2179 | return -1; 2180 | } 2181 | 2182 | if (!PyLong_Check(obj)) { 2183 | Py_DECREF(obj); 2184 | PyErr_Format(PyExc_TypeError, "config option %s is not an int", name); 2185 | return -1; 2186 | } 2187 | 2188 | int as_int = PyLong_AsInt(obj); 2189 | Py_DECREF(obj); 2190 | if (as_int == -1 && PyErr_Occurred()) { 2191 | PyErr_Format(PyExc_OverflowError, 2192 | "config option %s value does not fit into a C int", name); 2193 | return -1; 2194 | } 2195 | 2196 | *value = as_int; 2197 | return 0; 2198 | } 2199 | #endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) 2200 | 2201 | 2202 | #if PY_VERSION_HEX < 0x030F0000 2203 | static inline PyObject* 2204 | PySys_GetAttrString(const char *name) 2205 | { 2206 | #if PY_VERSION_HEX >= 0x03000000 2207 | PyObject *value = Py_XNewRef(PySys_GetObject(name)); 2208 | #else 2209 | PyObject *value = Py_XNewRef(PySys_GetObject((char*)name)); 2210 | #endif 2211 | if (value != NULL) { 2212 | return value; 2213 | } 2214 | if (!PyErr_Occurred()) { 2215 | PyErr_Format(PyExc_RuntimeError, "lost sys.%s", name); 2216 | } 2217 | return NULL; 2218 | } 2219 | 2220 | static inline PyObject* 2221 | PySys_GetAttr(PyObject *name) 2222 | { 2223 | #if PY_VERSION_HEX >= 0x03000000 2224 | const char *name_str = PyUnicode_AsUTF8(name); 2225 | #else 2226 | const char *name_str = PyString_AsString(name); 2227 | #endif 2228 | if (name_str == NULL) { 2229 | return NULL; 2230 | } 2231 | 2232 | return PySys_GetAttrString(name_str); 2233 | } 2234 | 2235 | static inline int 2236 | PySys_GetOptionalAttrString(const char *name, PyObject **value) 2237 | { 2238 | #if PY_VERSION_HEX >= 0x03000000 2239 | *value = Py_XNewRef(PySys_GetObject(name)); 2240 | #else 2241 | *value = Py_XNewRef(PySys_GetObject((char*)name)); 2242 | #endif 2243 | if (*value != NULL) { 2244 | return 1; 2245 | } 2246 | return 0; 2247 | } 2248 | 2249 | static inline int 2250 | PySys_GetOptionalAttr(PyObject *name, PyObject **value) 2251 | { 2252 | #if PY_VERSION_HEX >= 0x03000000 2253 | const char *name_str = PyUnicode_AsUTF8(name); 2254 | #else 2255 | const char *name_str = PyString_AsString(name); 2256 | #endif 2257 | if (name_str == NULL) { 2258 | *value = NULL; 2259 | return -1; 2260 | } 2261 | 2262 | return PySys_GetOptionalAttrString(name_str, value); 2263 | } 2264 | #endif // PY_VERSION_HEX < 0x030F00A1 2265 | 2266 | 2267 | #ifdef __cplusplus 2268 | } 2269 | #endif 2270 | #endif // PYTHONCAPI_COMPAT 2271 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 -u 2 | """ 3 | Run the test suite on multiple Python versions. 4 | 5 | Usage:: 6 | 7 | python3 runtests.py 8 | python3 runtests.py --verbose 9 | python3 runtests.py --current --verbose 10 | """ 11 | from __future__ import absolute_import 12 | from __future__ import print_function 13 | import argparse 14 | import os.path 15 | import shutil 16 | import sys 17 | try: 18 | from shutil import which 19 | except ImportError: 20 | # Python 2 21 | from distutils.spawn import find_executable as which 22 | 23 | 24 | from tests.utils import run_command 25 | 26 | 27 | TEST_DIR = os.path.join(os.path.dirname(__file__), 'tests') 28 | TEST_COMPAT = os.path.join(TEST_DIR, "test_pythoncapi_compat.py") 29 | TEST_UPGRADE = os.path.join(TEST_DIR, "test_upgrade_pythoncapi.py") 30 | 31 | PYTHONS = ( 32 | # CPython 33 | "python3-debug", 34 | "python3", 35 | "python2.7", 36 | "python3.6", 37 | "python3.7", 38 | "python3.8", 39 | "python3.9", 40 | "python3.10", 41 | "python3.11", 42 | "python3.12", 43 | "python3.13", 44 | 45 | # PyPy 46 | "pypy", 47 | "pypy2", 48 | "pypy2.7", 49 | "pypy3", 50 | "pypy3.6", 51 | "pypy3.7", 52 | "pypy3.8", 53 | "pypy3.9", 54 | "pypy3.10", 55 | "pypy3.11", 56 | ) 57 | 58 | 59 | def run_tests_exe(executable, verbose, tested): 60 | tested_key = os.path.realpath(executable) 61 | if tested_key in tested: 62 | return 63 | 64 | # Don't use realpath() for the executed command to support virtual 65 | # environments 66 | cmd = [executable, TEST_COMPAT] 67 | if verbose: 68 | cmd.append('-v') 69 | run_command(cmd) 70 | tested.add(tested_key) 71 | 72 | 73 | def run_tests(python, verbose, tested): 74 | executable = which(python) 75 | if not executable: 76 | print("Ignore missing Python executable: %s" % python) 77 | return 78 | run_tests_exe(executable, verbose, tested) 79 | 80 | 81 | def parse_args(): 82 | parser = argparse.ArgumentParser() 83 | parser.add_argument('-v', '--verbose', action="store_true", 84 | help='Verbose mode') 85 | parser.add_argument('-c', '--current', action="store_true", 86 | help="Only test the current Python executable " 87 | "(don't test multiple Python versions)") 88 | return parser.parse_args() 89 | 90 | 91 | def main(): 92 | args = parse_args() 93 | 94 | path = os.path.join(TEST_DIR, 'build') 95 | if os.path.exists(path): 96 | shutil.rmtree(path) 97 | 98 | # upgrade_pythoncapi.py requires Python 3.6 or newer 99 | if sys.version_info >= (3, 6): 100 | print("Run %s" % TEST_UPGRADE) 101 | cmd = [sys.executable, TEST_UPGRADE] 102 | if args.verbose: 103 | cmd.append('-v') 104 | run_command(cmd) 105 | else: 106 | print("Don't test upgrade_pythoncapi.py: it requires Python 3.6") 107 | print() 108 | 109 | tested = set() 110 | if not args.current: 111 | for python in PYTHONS: 112 | run_tests(python, args.verbose, tested) 113 | run_tests_exe(sys.executable, args.verbose, tested) 114 | 115 | print() 116 | print("Tested: %s Python executables" % len(tested)) 117 | else: 118 | run_tests_exe(sys.executable, args.verbose, tested) 119 | 120 | 121 | if __name__ == "__main__": 122 | main() 123 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python/pythoncapi-compat/fde4d3457d7c1e6f7d09afeae5afd2218fbb2cae/tests/__init__.py -------------------------------------------------------------------------------- /tests/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os.path 3 | import shlex 4 | import sys 5 | try: 6 | from setuptools import setup, Extension 7 | except ImportError: 8 | from distutils.core import setup, Extension 9 | try: 10 | from distutils import sysconfig 11 | except ImportError: 12 | import sysconfig 13 | 14 | 15 | # C++ is only supported on Python 3.6 and newer 16 | TEST_CXX = (sys.version_info >= (3, 6)) 17 | 18 | SRC_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) 19 | 20 | # Windows uses MSVC compiler 21 | MSVC = (os.name == "nt") 22 | 23 | COMMON_FLAGS = [ 24 | '-I' + SRC_DIR, 25 | ] 26 | if not MSVC: 27 | # C compiler flags for GCC and clang 28 | COMMON_FLAGS.extend(( 29 | # Treat warnings as error 30 | '-Werror', 31 | # Enable all warnings 32 | '-Wall', '-Wextra', 33 | # Extra warnings 34 | '-Wconversion', 35 | # /usr/lib64/pypy3.7/include/pyport.h:68:20: error: redefinition of typedef 36 | # 'Py_hash_t' is a C11 feature 37 | "-Wno-typedef-redefinition", 38 | )) 39 | CFLAGS = COMMON_FLAGS + [ 40 | # Use C99 for pythoncapi_compat.c which initializes PyModuleDef with a 41 | # mixture of designated and non-designated initializers 42 | '-std=c99', 43 | ] 44 | else: 45 | # C compiler flags for MSVC 46 | COMMON_FLAGS.extend(( 47 | # Treat all compiler warnings as compiler errors 48 | '/WX', 49 | )) 50 | CFLAGS = list(COMMON_FLAGS) 51 | CXXFLAGS = list(COMMON_FLAGS) 52 | 53 | 54 | def main(): 55 | # gh-105776: When "gcc -std=11" is used as the C++ compiler, -std=c11 56 | # option emits a C++ compiler warning. Remove "-std11" option from the 57 | # CC command. 58 | cmd = (sysconfig.get_config_var('CC') or '') 59 | if cmd: 60 | cmd = shlex.split(cmd) 61 | cmd = [arg for arg in cmd if not arg.startswith('-std=')] 62 | if (sys.version_info >= (3, 8)): 63 | cmd = shlex.join(cmd) 64 | elif (sys.version_info >= (3, 3)): 65 | cmd = ' '.join(shlex.quote(arg) for arg in cmd) 66 | else: 67 | # Python 2.7 68 | import pipes 69 | cmd = ' '.join(pipes.quote(arg) for arg in cmd) 70 | # CC env var overrides sysconfig CC variable in setuptools 71 | os.environ['CC'] = cmd 72 | 73 | # C extension 74 | c_ext = Extension( 75 | 'test_pythoncapi_compat_cext', 76 | sources=['test_pythoncapi_compat_cext.c'], 77 | extra_compile_args=CFLAGS) 78 | extensions = [c_ext] 79 | 80 | if TEST_CXX: 81 | # C++ extension 82 | 83 | # MSVC has /std flag but doesn't support /std:c++11 84 | if not MSVC: 85 | versions = [ 86 | ('test_pythoncapi_compat_cpp03ext', ['-std=c++03']), 87 | ('test_pythoncapi_compat_cpp11ext', ['-std=c++11']), 88 | ] 89 | else: 90 | versions = [ 91 | ('test_pythoncapi_compat_cppext', None), 92 | ('test_pythoncapi_compat_cpp14ext', ['/std:c++14', '/Zc:__cplusplus']), 93 | ] 94 | for name, std_flags in versions: 95 | flags = list(CXXFLAGS) 96 | if std_flags is not None: 97 | flags.extend(std_flags) 98 | cpp_ext = Extension( 99 | name, 100 | sources=['test_pythoncapi_compat_cppext.cpp'], 101 | extra_compile_args=flags, 102 | language='c++') 103 | extensions.append(cpp_ext) 104 | 105 | setup(name="test_pythoncapi_compat", 106 | ext_modules=extensions) 107 | 108 | 109 | if __name__ == "__main__": 110 | main() 111 | -------------------------------------------------------------------------------- /tests/test_pythoncapi_compat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Run the test suite. 4 | 5 | Usage:: 6 | 7 | python3 run_tests.py 8 | python3 run_tests.py -v # verbose mode 9 | """ 10 | from __future__ import absolute_import 11 | from __future__ import print_function 12 | import gc 13 | import os.path 14 | import shutil 15 | import subprocess 16 | import sys 17 | try: 18 | import faulthandler 19 | except ImportError: 20 | # Python 2 21 | faulthandler = None 22 | 23 | # test.utils 24 | from utils import run_command, command_stdout 25 | 26 | 27 | # Windows uses MSVC compiler 28 | MSVC = (os.name == "nt") 29 | 30 | TESTS = [ 31 | ("test_pythoncapi_compat_cext", "C"), 32 | ] 33 | if not MSVC: 34 | TESTS.extend(( 35 | ("test_pythoncapi_compat_cpp03ext", "C++03"), 36 | ("test_pythoncapi_compat_cpp11ext", "C++11"), 37 | )) 38 | else: 39 | TESTS.extend(( 40 | ("test_pythoncapi_compat_cppext", "C++"), 41 | ("test_pythoncapi_compat_cpp14ext", "C++14"), 42 | )) 43 | 44 | 45 | VERBOSE = False 46 | 47 | 48 | def display_title(title): 49 | if not VERBOSE: 50 | return 51 | 52 | ver = sys.version_info 53 | title = "Python %s.%s: %s" % (ver.major, ver.minor, title) 54 | 55 | print(title) 56 | print("=" * len(title)) 57 | print() 58 | sys.stdout.flush() 59 | 60 | 61 | def build_ext(): 62 | display_title("Build test extensions") 63 | if os.path.exists("build"): 64 | shutil.rmtree("build") 65 | cmd = [sys.executable, "setup.py", "build"] 66 | if VERBOSE: 67 | run_command(cmd) 68 | print() 69 | else: 70 | exitcode, stdout = command_stdout(cmd, stderr=subprocess.STDOUT) 71 | if exitcode: 72 | print(stdout.rstrip()) 73 | sys.exit(exitcode) 74 | 75 | 76 | def import_tests(module_name): 77 | pythonpath = None 78 | for name in os.listdir("build"): 79 | if name.startswith('lib.'): 80 | pythonpath = os.path.join("build", name) 81 | 82 | if not pythonpath: 83 | raise Exception("Failed to find the build directory") 84 | sys.path.append(pythonpath) 85 | 86 | return __import__(module_name) 87 | 88 | 89 | def _run_tests(tests, verbose): 90 | for name, test_func in tests: 91 | if verbose: 92 | print("%s()" % name) 93 | sys.stdout.flush() 94 | test_func() 95 | 96 | 97 | _HAS_CLEAR_TYPE_CACHE = hasattr(sys, '_clear_type_cache') 98 | 99 | def _refleak_cleanup(): 100 | if _HAS_CLEAR_TYPE_CACHE: 101 | sys._clear_type_cache() 102 | gc.collect() 103 | 104 | 105 | def _check_refleak(test_func, verbose): 106 | nrun = 6 107 | for i in range(1, nrun + 1): 108 | if verbose: 109 | if i > 1: 110 | print() 111 | print("Run %s/%s:" % (i, nrun)) 112 | sys.stdout.flush() 113 | 114 | init_refcnt = sys.gettotalrefcount() 115 | test_func() 116 | _refleak_cleanup() 117 | diff = sys.gettotalrefcount() - init_refcnt 118 | 119 | if i > 3 and diff: 120 | raise AssertionError("refcnt leak, diff: %s" % diff) 121 | 122 | 123 | def python_version(): 124 | ver = sys.version_info 125 | build = 'debug' if hasattr(sys, 'gettotalrefcount') else 'release' 126 | if hasattr(sys, 'implementation'): 127 | python_impl = sys.implementation.name 128 | if python_impl == 'cpython': 129 | python_impl = 'CPython' 130 | elif python_impl == 'pypy': 131 | python_impl = 'PyPy' 132 | else: 133 | if "PyPy" in sys.version: 134 | python_impl = "PyPy" 135 | else: 136 | python_impl = 'Python' 137 | return "%s %s.%s (%s build)" % (python_impl, ver.major, ver.minor, build) 138 | 139 | 140 | def run_tests(module_name, lang): 141 | title = "Test %s (%s)" % (module_name, lang) 142 | display_title(title) 143 | 144 | try: 145 | testmod = import_tests(module_name) 146 | except ImportError: 147 | # The C extension must always be available 148 | if lang == "C": 149 | raise 150 | 151 | if VERBOSE: 152 | print("%s: skip %s, missing %s extension" 153 | % (python_version(), lang, module_name)) 154 | print() 155 | return 156 | 157 | if VERBOSE: 158 | empty_line = False 159 | for attr in ('__cplusplus', 'PY_VERSION', 'PY_VERSION_HEX', 160 | 'PYPY_VERSION', 'PYPY_VERSION_NUM'): 161 | try: 162 | value = getattr(testmod, attr) 163 | except AttributeError: 164 | pass 165 | else: 166 | if attr in ("PY_VERSION_HEX", "PYPY_VERSION_NUM"): 167 | value = "0x%x" % value 168 | print("%s: %s" % (attr, value)) 169 | empty_line = True 170 | 171 | if empty_line: 172 | print() 173 | 174 | check_refleak = hasattr(sys, 'gettotalrefcount') 175 | 176 | tests = [(name, getattr(testmod, name)) 177 | for name in dir(testmod) 178 | if name.startswith("test")] 179 | 180 | def test_func(): 181 | _run_tests(tests, VERBOSE) 182 | 183 | if check_refleak: 184 | _check_refleak(test_func, VERBOSE) 185 | else: 186 | test_func() 187 | 188 | if VERBOSE: 189 | print() 190 | 191 | msg = "%s %s tests succeeded!" % (len(tests), lang) 192 | msg = "%s: %s" % (python_version(), msg) 193 | if check_refleak: 194 | msg = "%s (no reference leak detected)" % msg 195 | print(msg) 196 | 197 | 198 | def main(): 199 | global VERBOSE 200 | VERBOSE = ("-v" in sys.argv[1:] or "--verbose" in sys.argv[1:]) 201 | 202 | if (3, 13) <= sys.version_info <= (3, 13, 0, 'alpha', 4): 203 | print("SKIP Python 3.13 alpha 1..4: not supported!") 204 | return 205 | 206 | if faulthandler is not None: 207 | faulthandler.enable() 208 | 209 | src_dir = os.path.dirname(__file__) 210 | if src_dir: 211 | os.chdir(src_dir) 212 | 213 | build_ext() 214 | 215 | for module_name, lang in TESTS: 216 | run_tests(module_name, lang) 217 | 218 | 219 | if __name__ == "__main__": 220 | main() 221 | -------------------------------------------------------------------------------- /tests/test_pythoncapi_compat_cppext.cpp: -------------------------------------------------------------------------------- 1 | // C++ flavor of the C extension. 2 | // Reuse the whole C source code using an #include, but the file has ".cpp" 3 | // extension to use a C++ builder. 4 | #include "test_pythoncapi_compat_cext.c" 5 | -------------------------------------------------------------------------------- /tests/test_upgrade_pythoncapi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import io 3 | import os 4 | import sys 5 | import tempfile 6 | import textwrap 7 | import unittest 8 | 9 | # Get upgrade_pythoncapi.py of the parent directory 10 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) 11 | import upgrade_pythoncapi # noqa 12 | 13 | 14 | def operations(disable=None): 15 | if isinstance(disable, str): 16 | disable = (disable,) 17 | elif not disable: 18 | disable = () 19 | operations = ["all"] 20 | for op in upgrade_pythoncapi.EXCLUDE_FROM_ALL: 21 | if op.NAME in disable: 22 | continue 23 | operations.append(op.NAME) 24 | for name in disable: 25 | operations.append(f'-{name}') 26 | operations = ','.join(operations) 27 | return operations 28 | 29 | 30 | def patch(source, no_compat=False, disable=None): 31 | args = ['script', 'mod.c', '-o', operations(disable=disable)] 32 | if no_compat: 33 | args.append('--no-compat') 34 | 35 | patcher = upgrade_pythoncapi.Patcher(args) 36 | return patcher.patch(source) 37 | 38 | 39 | def reformat(source): 40 | return textwrap.dedent(source).strip() 41 | 42 | 43 | class Tests(unittest.TestCase): 44 | maxDiff = 80 * 30 45 | 46 | def _patch_file(self, source, tmp_dir=None): 47 | # test Patcher.patcher() 48 | filename = tempfile.mktemp(suffix='.c', dir=tmp_dir) 49 | old_filename = filename + ".old" 50 | try: 51 | with open(filename, "w", encoding="utf-8", newline="") as fp: 52 | fp.write(source) 53 | 54 | old_stderr = sys.stderr 55 | old_argv = list(sys.argv) 56 | try: 57 | # redirect stderr 58 | sys.stderr = io.StringIO() 59 | 60 | if tmp_dir is not None: 61 | arg = tmp_dir 62 | else: 63 | arg = filename 64 | sys.argv = ['script', arg] 65 | try: 66 | upgrade_pythoncapi.Patcher().main() 67 | except SystemExit as exc: 68 | self.assertEqual(exc.code, 0) 69 | else: 70 | self.fail("SystemExit not raised") 71 | finally: 72 | sys.stderr = old_stderr 73 | sys.argv = old_argv 74 | 75 | with open(filename, encoding="utf-8", newline="") as fp: 76 | new_contents = fp.read() 77 | 78 | with open(old_filename, encoding="utf-8", newline="") as fp: 79 | old_contents = fp.read() 80 | finally: 81 | try: 82 | os.unlink(filename) 83 | except FileNotFoundError: 84 | pass 85 | try: 86 | os.unlink(old_filename) 87 | except FileNotFoundError: 88 | pass 89 | 90 | self.assertEqual(old_contents, source) 91 | return new_contents 92 | 93 | def test_patch_file(self): 94 | source = """ 95 | PyTypeObject* 96 | test_type(PyObject *obj, PyTypeObject *type) 97 | { 98 | Py_TYPE(obj) = type; 99 | return Py_TYPE(obj); 100 | } 101 | """ 102 | expected = """ 103 | #include "pythoncapi_compat.h" 104 | 105 | PyTypeObject* 106 | test_type(PyObject *obj, PyTypeObject *type) 107 | { 108 | Py_SET_TYPE(obj, type); 109 | return Py_TYPE(obj); 110 | } 111 | """ 112 | source = reformat(source) 113 | expected = reformat(expected) 114 | 115 | new_contents = self._patch_file(source) 116 | self.assertEqual(new_contents, expected) 117 | 118 | with tempfile.TemporaryDirectory() as tmp_dir: 119 | new_contents = self._patch_file(source, tmp_dir) 120 | self.assertEqual(new_contents, expected) 121 | 122 | def test_patch_file_preserve_newlines(self): 123 | source = """ 124 | Py_ssize_t get_size(PyVarObject *obj)\r\n\ 125 | \n\ 126 | { return obj->ob_size; }\r\ 127 | """ 128 | expected = """ 129 | Py_ssize_t get_size(PyVarObject *obj)\r\n\ 130 | \n\ 131 | { return Py_SIZE(obj); }\r\ 132 | """ 133 | source = reformat(source) 134 | expected = reformat(expected) 135 | new_contents = self._patch_file(source) 136 | self.assertEqual(new_contents, expected) 137 | 138 | def check_replace(self, source, expected, **kwargs): 139 | source = reformat(source) 140 | expected = reformat(expected) 141 | self.assertEqual(patch(source, **kwargs), expected) 142 | 143 | def check_dont_replace(self, source, disable=None): 144 | source = reformat(source) 145 | self.assertEqual(patch(source, disable=disable), source) 146 | 147 | def test_expr_regex(self): 148 | # Test EXPR_REGEX 149 | self.check_replace("a->b->ob_type", "Py_TYPE(a->b)") 150 | self.check_replace("a.b->ob_type", "Py_TYPE(a.b)") 151 | self.check_replace("array[2]->ob_type", "Py_TYPE(array[2])") 152 | 153 | # Don't match function calls 154 | self.check_dont_replace("func()->ob_type") 155 | 156 | def test_pythoncapi_compat(self): 157 | # If pythoncapi_compat.h is included, avoid compatibility includes 158 | # and macros. 159 | # 160 | # Otherise, Py_SET_TYPE() requires a macro and PyFrame_GetBack() 161 | # requires 2 macros and an include. 162 | HEADERS = ( 163 | '', 164 | '"pythoncapi_compat.h"', 165 | ) 166 | for header in HEADERS: 167 | # There is no empty line between the include and the function 168 | # on purpose. 169 | self.check_replace(""" 170 | #include %s 171 | void test_set_type(PyObject *obj, PyTypeObject *type) 172 | { 173 | Py_TYPE(obj) = type; 174 | } 175 | PyFrameObject* frame_back_borrowed(PyFrameObject *frame) 176 | { 177 | return frame->f_back; 178 | } 179 | """ % header, """ 180 | #include %s 181 | void test_set_type(PyObject *obj, PyTypeObject *type) 182 | { 183 | Py_SET_TYPE(obj, type); 184 | } 185 | PyFrameObject* frame_back_borrowed(PyFrameObject *frame) 186 | { 187 | return _PyFrame_GetBackBorrow(frame); 188 | } 189 | """ % header) 190 | 191 | def test_py_type(self): 192 | source = """ 193 | PyTypeObject* get_type(PyObject *obj) 194 | { return obj->ob_type; } 195 | """ 196 | expected = """ 197 | PyTypeObject* get_type(PyObject *obj) 198 | { return Py_TYPE(obj); } 199 | """ 200 | self.check_replace(source, expected) 201 | 202 | def test_py_size(self): 203 | source = """ 204 | Py_ssize_t get_size(PyVarObject *obj) 205 | { return obj->ob_size; } 206 | """ 207 | expected = """ 208 | Py_ssize_t get_size(PyVarObject *obj) 209 | { return Py_SIZE(obj); } 210 | """ 211 | self.check_replace(source, expected) 212 | 213 | def test_py_refcnt(self): 214 | source = """ 215 | Py_ssize_t get_refcnt(PyObject *obj) 216 | { return obj->ob_refcnt; } 217 | """ 218 | expected = """ 219 | Py_ssize_t get_refcnt(PyObject *obj) 220 | { return Py_REFCNT(obj); } 221 | """ 222 | self.check_replace(source, expected) 223 | 224 | def test_py_set_type(self): 225 | source = """ 226 | void test_type(PyObject *obj, PyTypeObject *type) 227 | { 228 | obj->ob_type = type; 229 | Py_TYPE(obj) = type; 230 | } 231 | """ 232 | expected = """ 233 | #include "pythoncapi_compat.h" 234 | 235 | void test_type(PyObject *obj, PyTypeObject *type) 236 | { 237 | Py_SET_TYPE(obj, type); 238 | Py_SET_TYPE(obj, type); 239 | } 240 | """ 241 | self.check_replace(source, expected) 242 | 243 | self.check_dont_replace(""" 244 | PyTypeObject* get_type(PyObject *obj, PyTypeObject *check_type) 245 | { 246 | assert(Py_TYPE(args) == check_type); 247 | return Py_TYPE(obj); 248 | } 249 | """) 250 | 251 | def test_py_set_size(self): 252 | source = """\ 253 | void test_size(PyVarObject *obj) 254 | { 255 | obj->ob_size = 3; 256 | Py_SIZE(obj) = 4; 257 | } 258 | """ 259 | expected = """\ 260 | #include "pythoncapi_compat.h" 261 | 262 | void test_size(PyVarObject *obj) 263 | { 264 | Py_SET_SIZE(obj, 3); 265 | Py_SET_SIZE(obj, 4); 266 | } 267 | """ 268 | self.check_replace(source, expected) 269 | 270 | self.check_dont_replace(""" 271 | Py_ssize_t 272 | get_size(PyObject *obj) 273 | { 274 | assert(Py_SIZE(args) == 1); 275 | return Py_SIZE(obj); 276 | } 277 | """) 278 | 279 | def test_py_set_refcnt(self): 280 | source = """\ 281 | void set_refcnt(PyObject *obj) 282 | { 283 | obj->ob_refcnt = 1; 284 | Py_REFCNT(obj) = 2; 285 | } 286 | """ 287 | expected = """\ 288 | #include "pythoncapi_compat.h" 289 | 290 | void set_refcnt(PyObject *obj) 291 | { 292 | Py_SET_REFCNT(obj, 1); 293 | Py_SET_REFCNT(obj, 2); 294 | } 295 | """ 296 | self.check_replace(source, expected) 297 | 298 | self.check_dont_replace(""" 299 | Py_ssize_t 300 | get_refcnt(PyObject *obj) 301 | { 302 | assert(Py_REFCNT(args) == 1); 303 | return Py_REFCNT(obj); 304 | } 305 | """) 306 | 307 | def test_pyobject_new(self): 308 | source = """\ 309 | capsule = PyObject_NEW(PyCapsule, &PyCapsule_Type); 310 | pattern = PyObject_NEW_VAR(PatternObject, Pattern_Type, n); 311 | """ 312 | expected = """\ 313 | capsule = PyObject_New(PyCapsule, &PyCapsule_Type); 314 | pattern = PyObject_NewVar(PatternObject, Pattern_Type, n); 315 | """ 316 | self.check_replace(source, expected) 317 | 318 | self.check_dont_replace(""" 319 | func = PyObject_NEW; 320 | capsule2 = PyObject_NEW2(PyCapsule, &PyCapsule_Type); 321 | 322 | func2 = PyObject_NEW_VAR; 323 | pattern2 = PyObject_NEW_VAR2(PatternObject, Pattern_Type, n); 324 | """) 325 | 326 | def test_pymem_malloc(self): 327 | source = """\ 328 | void *ptr = PyMem_MALLOC(10); 329 | ptr = PyMem_REALLOC(ptr, 20); 330 | PyMem_FREE(ptr); 331 | PyMem_DEL(ptr); 332 | PyMem_Del(ptr); 333 | """ 334 | expected = """\ 335 | void *ptr = PyMem_Malloc(10); 336 | ptr = PyMem_Realloc(ptr, 20); 337 | PyMem_Free(ptr); 338 | PyMem_Free(ptr); 339 | PyMem_Free(ptr); 340 | """ 341 | self.check_replace(source, expected) 342 | 343 | def test_pyobject_malloc(self): 344 | source = """\ 345 | void *ptr = PyObject_MALLOC(10); 346 | ptr = PyObject_REALLOC(ptr, 20); 347 | PyObject_FREE(ptr); 348 | PyObject_DEL(ptr); 349 | PyObject_Del(ptr); 350 | """ 351 | expected = """\ 352 | void *ptr = PyObject_Malloc(10); 353 | ptr = PyObject_Realloc(ptr, 20); 354 | PyObject_Free(ptr); 355 | PyObject_Free(ptr); 356 | PyObject_Free(ptr); 357 | """ 358 | self.check_replace(source, expected) 359 | 360 | def test_pyframe_getback(self): 361 | source = """\ 362 | PyFrameObject* frame_back_borrowed(PyFrameObject *frame) 363 | { 364 | return frame->f_back; 365 | } 366 | """ 367 | expected = """\ 368 | #include "pythoncapi_compat.h" 369 | 370 | PyFrameObject* frame_back_borrowed(PyFrameObject *frame) 371 | { 372 | return _PyFrame_GetBackBorrow(frame); 373 | } 374 | """ 375 | self.check_replace(source, expected) 376 | 377 | def test_pyframe_getcode(self): 378 | self.check_replace("""\ 379 | PyCodeObject* frame_code_borrowed(PyFrameObject *frame) 380 | { 381 | return frame->f_code; 382 | } 383 | """, """\ 384 | #include "pythoncapi_compat.h" 385 | 386 | PyCodeObject* frame_code_borrowed(PyFrameObject *frame) 387 | { 388 | return _PyFrame_GetCodeBorrow(frame); 389 | } 390 | """) 391 | 392 | def test_get_member_regex(self): 393 | # Use PyFrame_GetCode() to test get_member_regex() 394 | self.check_dont_replace(""" 395 | void frame_set_code(PyFrameObject *frame, PyCodeObject *code) 396 | { 397 | frame->f_code = code; 398 | } 399 | 400 | void frame_clear_code(PyFrameObject *frame) 401 | { 402 | Py_CLEAR(frame->f_code); 403 | } 404 | """) 405 | 406 | def test_pythreadstate_getinterpreter(self): 407 | self.check_replace(""" 408 | PyInterpreterState* get_interp(PyThreadState *tstate) 409 | { return tstate->interp; } 410 | """, """ 411 | #include "pythoncapi_compat.h" 412 | 413 | PyInterpreterState* get_interp(PyThreadState *tstate) 414 | { return PyThreadState_GetInterpreter(tstate); } 415 | """) 416 | 417 | def test_pythreadstate_getframe(self): 418 | self.check_replace(""" 419 | PyFrameObject* get_frame(PyThreadState *tstate) 420 | { return tstate->frame; } 421 | """, """ 422 | #include "pythoncapi_compat.h" 423 | 424 | PyFrameObject* get_frame(PyThreadState *tstate) 425 | { return _PyThreadState_GetFrameBorrow(tstate); } 426 | """) 427 | 428 | def test_py_newref_return(self): 429 | self.check_replace(""" 430 | PyObject* new_ref(PyObject *obj) { 431 | Py_INCREF(obj); 432 | return obj; 433 | } 434 | 435 | PyObject* same_line(PyObject *obj) { 436 | Py_INCREF(obj); return obj; 437 | } 438 | 439 | PyObject* new_xref(PyObject *obj) { 440 | Py_XINCREF(obj); 441 | return obj; 442 | } 443 | 444 | PyObject* cast(PyLongObject *obj) { 445 | Py_XINCREF(obj); 446 | return (PyObject *)obj; 447 | } 448 | """, """ 449 | #include "pythoncapi_compat.h" 450 | 451 | PyObject* new_ref(PyObject *obj) { 452 | return Py_NewRef(obj); 453 | } 454 | 455 | PyObject* same_line(PyObject *obj) { 456 | return Py_NewRef(obj); 457 | } 458 | 459 | PyObject* new_xref(PyObject *obj) { 460 | return Py_XNewRef(obj); 461 | } 462 | 463 | PyObject* cast(PyLongObject *obj) { 464 | return Py_XNewRef(obj); 465 | } 466 | """) 467 | 468 | def test_py_newref(self): 469 | # INCREF, assign 470 | self.check_replace(""" 471 | void set_attr(MyStruct *obj, PyObject *value, int test) 472 | { 473 | // 1 474 | Py_INCREF(value); 475 | obj->attr = value; 476 | // 2 477 | obj->attr = value; 478 | Py_INCREF(value); 479 | // 3 480 | obj->attr = value; 481 | Py_INCREF(obj->attr); 482 | } 483 | """, """ 484 | #include "pythoncapi_compat.h" 485 | 486 | void set_attr(MyStruct *obj, PyObject *value, int test) 487 | { 488 | // 1 489 | obj->attr = Py_NewRef(value); 490 | // 2 491 | obj->attr = Py_NewRef(value); 492 | // 3 493 | obj->attr = Py_NewRef(value); 494 | } 495 | """) 496 | 497 | # Same line 498 | self.check_replace(""" 499 | void set_attr(MyStruct *obj, PyObject *value, int test) 500 | { 501 | // same line 1 502 | obj->attr = value; Py_INCREF(value); 503 | // same line 2 504 | if (test) { obj->attr = value; Py_INCREF(obj->attr); } 505 | // same line 3 506 | if (test) { Py_INCREF(value); obj->attr = value; } 507 | } 508 | """, """ 509 | #include "pythoncapi_compat.h" 510 | 511 | void set_attr(MyStruct *obj, PyObject *value, int test) 512 | { 513 | // same line 1 514 | obj->attr = Py_NewRef(value); 515 | // same line 2 516 | if (test) { obj->attr = Py_NewRef(value); } 517 | // same line 3 518 | if (test) { obj->attr = Py_NewRef(value); } 519 | } 520 | """) 521 | 522 | # Cast 523 | self.check_replace(""" 524 | void set_attr(MyStruct *obj, PyObject *value, int test) 525 | { 526 | // cast 1 527 | Py_INCREF(value); 528 | obj->attr = (PyObject*)value; 529 | // cast 2 530 | obj->attr = (PyObject*)value; 531 | Py_INCREF(value); 532 | 533 | // assign var, incref 534 | PyCodeObject *code_obj = (PyCodeObject *)code; 535 | Py_INCREF(code_obj); 536 | // assign var, incref 537 | PyCodeObject* code_obj = (PyCodeObject *)code; 538 | Py_INCREF(code); 539 | // assign var, xincref 540 | PyCodeObject * code_obj = (PyCodeObject *)code; 541 | Py_XINCREF(code_obj); 542 | 543 | // incref, assign var 544 | Py_INCREF(code); 545 | PyCodeObject* code_obj = (PyCodeObject *)code; 546 | // xincref, assign var 547 | Py_XINCREF(code); 548 | PyCodeObject *code_obj = (PyCodeObject *)code; 549 | } 550 | """, """ 551 | #include "pythoncapi_compat.h" 552 | 553 | void set_attr(MyStruct *obj, PyObject *value, int test) 554 | { 555 | // cast 1 556 | obj->attr = Py_NewRef(value); 557 | // cast 2 558 | obj->attr = Py_NewRef(value); 559 | 560 | // assign var, incref 561 | PyCodeObject *code_obj = (PyCodeObject *)Py_NewRef(code); 562 | // assign var, incref 563 | PyCodeObject* code_obj = (PyCodeObject *)Py_NewRef(code); 564 | // assign var, xincref 565 | PyCodeObject * code_obj = (PyCodeObject *)Py_XNewRef(code); 566 | 567 | // incref, assign var 568 | PyCodeObject* code_obj = (PyCodeObject *)Py_NewRef(code); 569 | // xincref, assign var 570 | PyCodeObject *code_obj = (PyCodeObject *)Py_XNewRef(code); 571 | } 572 | """) 573 | 574 | # Py_XINCREF 575 | self.check_replace(""" 576 | void set_xattr(MyStruct *obj, PyObject *value) 577 | { 578 | // 1 579 | Py_XINCREF(value); 580 | obj->attr = value; 581 | // 2 582 | obj->attr = value; 583 | Py_XINCREF(value); 584 | // 3 585 | obj->attr = value; 586 | Py_XINCREF(obj->attr); 587 | } 588 | """, """ 589 | #include "pythoncapi_compat.h" 590 | 591 | void set_xattr(MyStruct *obj, PyObject *value) 592 | { 593 | // 1 594 | obj->attr = Py_XNewRef(value); 595 | // 2 596 | obj->attr = Py_XNewRef(value); 597 | // 3 598 | obj->attr = Py_XNewRef(value); 599 | } 600 | """) 601 | 602 | # the first Py_INCREF should be replaced before the second one, 603 | # otherwise the first Py_INCREF is not replaced. 604 | self.check_replace(""" 605 | void set(void) 606 | { 607 | PyObject *x, *y; 608 | Py_INCREF(Py_None); 609 | x = Py_None; 610 | Py_INCREF(Py_None); 611 | x = Py_None; 612 | Py_DECREF(x); 613 | Py_DECREF(y); 614 | } 615 | """, """ 616 | #include "pythoncapi_compat.h" 617 | 618 | void set(void) 619 | { 620 | PyObject *x, *y; 621 | x = Py_NewRef(Py_None); 622 | x = Py_NewRef(Py_None); 623 | Py_DECREF(x); 624 | Py_DECREF(y); 625 | } 626 | """) 627 | 628 | # Indentation matters for conditional code 629 | self.check_dont_replace(""" 630 | void test1(int test) 631 | { 632 | PyObject *res; 633 | if (test) 634 | res = Py_True; 635 | else 636 | res = Py_False; 637 | Py_INCREF(res); 638 | 639 | Py_DECREF(res); 640 | } 641 | 642 | int test2(struct datetime* result, PyObject *tzinfo) 643 | { 644 | int res = 0; 645 | if (test) 646 | res = 1; 647 | else 648 | Py_INCREF(tzinfo); 649 | result->tzinfo = tzinfo; 650 | return res; 651 | } 652 | """) 653 | 654 | def test_py_clear(self): 655 | self.check_replace(""" 656 | void clear(int test) 657 | { 658 | PyObject *obj; 659 | 660 | // two lines 661 | Py_XDECREF(obj); 662 | obj = NULL; 663 | 664 | // inside if 665 | if (test) { Py_XDECREF(obj); obj = NULL; } 666 | } 667 | """, """ 668 | void clear(int test) 669 | { 670 | PyObject *obj; 671 | 672 | // two lines 673 | Py_CLEAR(obj); 674 | 675 | // inside if 676 | if (test) { Py_CLEAR(obj); } 677 | } 678 | """) 679 | 680 | # Don't replace Py_DECREF() 681 | self.check_dont_replace(""" 682 | void dont_clear(void) 683 | { 684 | PyObject *obj; 685 | Py_DECREF(obj); 686 | obj = NULL; 687 | } 688 | """, disable="Py_SETREF") 689 | 690 | def test_py_setref(self): 691 | self.check_replace(""" 692 | void set(PyObject **obj, PyObject *t) 693 | { 694 | // DECREF 695 | Py_DECREF(*obj); 696 | *obj = t; 697 | 698 | // XDECREF 699 | Py_XDECREF(*obj); 700 | *obj = t; 701 | 702 | // DECREF, INCREF 703 | Py_DECREF(*obj); 704 | Py_INCREF(t); 705 | *obj = t; 706 | } 707 | """, """ 708 | #include "pythoncapi_compat.h" 709 | 710 | void set(PyObject **obj, PyObject *t) 711 | { 712 | // DECREF 713 | Py_SETREF(*obj, t); 714 | 715 | // XDECREF 716 | Py_XSETREF(*obj, t); 717 | 718 | // DECREF, INCREF 719 | Py_SETREF(*obj, Py_NewRef(t)); 720 | } 721 | """) 722 | 723 | self.check_replace(""" 724 | void set(PyObject **obj, PyObject *value) 725 | { 726 | // 1 727 | PyObject *old = *obj; 728 | *obj = value; 729 | Py_DECREF(old); 730 | // 2 731 | PyObject *old = *obj; 732 | *obj = Py_XNewRef(value); 733 | Py_DECREF(old); 734 | // 3 735 | PyObject *old = *obj; 736 | *obj = value; 737 | Py_XDECREF(old); 738 | // 4 739 | PyObject *old = *obj; 740 | *obj = Py_NewRef(value); 741 | Py_XDECREF(old); 742 | } 743 | """, """ 744 | #include "pythoncapi_compat.h" 745 | 746 | void set(PyObject **obj, PyObject *value) 747 | { 748 | // 1 749 | Py_SETREF(*obj, value); 750 | // 2 751 | Py_SETREF(*obj, Py_XNewRef(value)); 752 | // 3 753 | Py_XSETREF(*obj, value); 754 | // 4 755 | Py_XSETREF(*obj, Py_NewRef(value)); 756 | } 757 | """) 758 | 759 | # INCREF, DECREF, assign 760 | self.check_replace(""" 761 | void set(void) 762 | { 763 | // 1 764 | Py_INCREF(value); 765 | Py_DECREF(obj); 766 | obj = value; 767 | // 2 768 | Py_INCREF(value); 769 | Py_XDECREF(obj); 770 | obj = value; 771 | // 3 772 | Py_XINCREF(value); 773 | Py_DECREF(obj); 774 | obj = value; 775 | // 4 776 | Py_XINCREF(value); 777 | Py_XDECREF(obj); 778 | obj = value; 779 | } 780 | """, """ 781 | #include "pythoncapi_compat.h" 782 | 783 | void set(void) 784 | { 785 | // 1 786 | Py_SETREF(obj, Py_NewRef(value)); 787 | // 2 788 | Py_XSETREF(obj, Py_NewRef(value)); 789 | // 3 790 | Py_SETREF(obj, Py_XNewRef(value)); 791 | // 4 792 | Py_XSETREF(obj, Py_XNewRef(value)); 793 | } 794 | """) 795 | 796 | # old variable 797 | self.check_replace(""" 798 | void set(PyObject **obj, PyObject *value) 799 | { 800 | // 1 801 | PyObject *old_next = (PyObject*)self->tb_next; 802 | self->tb_next = (PyTracebackObject *)Py_XNewRef(new_next); 803 | Py_XDECREF(old_next); 804 | // 2 805 | old_next = (PyObject*)self->tb_next; 806 | self->tb_next = (PyTracebackObject *)Py_XNewRef(new_next); 807 | Py_XDECREF(old_next); 808 | } 809 | """, """ 810 | #include "pythoncapi_compat.h" 811 | 812 | void set(PyObject **obj, PyObject *value) 813 | { 814 | // 1 815 | Py_XSETREF(self->tb_next, (PyTracebackObject *)Py_XNewRef(new_next)); 816 | // 2 817 | Py_XSETREF(self->tb_next, (PyTracebackObject *)Py_XNewRef(new_next)); 818 | } 819 | """) 820 | 821 | # Py_CLEAR 822 | self.check_replace(""" 823 | void set(PyObject **obj, PyObject *value) 824 | { 825 | // 1 826 | Py_CLEAR(self->tb_next); 827 | self->tb_next = value; 828 | // 2 829 | Py_INCREF(value); 830 | Py_CLEAR(self->tb_next); 831 | self->tb_next = value; 832 | // 3 833 | Py_XINCREF(value); 834 | Py_CLEAR(self->tb_next); 835 | self->tb_next = value; 836 | } 837 | """, """ 838 | #include "pythoncapi_compat.h" 839 | 840 | void set(PyObject **obj, PyObject *value) 841 | { 842 | // 1 843 | Py_XSETREF(self->tb_next, value); 844 | // 2 845 | Py_XSETREF(self->tb_next, Py_NewRef(value)); 846 | // 3 847 | Py_XSETREF(self->tb_next, Py_XNewRef(value)); 848 | } 849 | """) 850 | 851 | def test_py_is(self): 852 | self.check_replace(""" 853 | void test_py_is(PyObject *x) 854 | { 855 | if (x == Py_None) { 856 | return 1; 857 | } 858 | if (x == Py_True) { 859 | return 2; 860 | } 861 | if (x == Py_False) { 862 | return 3; 863 | } 864 | return 0; 865 | } 866 | 867 | void test_py_is_not(PyObject *x) 868 | { 869 | if (x != Py_None) { 870 | return 1; 871 | } 872 | if (x != Py_True) { 873 | return 2; 874 | } 875 | if (x != Py_False) { 876 | return 3; 877 | } 878 | return 0; 879 | } 880 | """, """ 881 | #include "pythoncapi_compat.h" 882 | 883 | void test_py_is(PyObject *x) 884 | { 885 | if (Py_IsNone(x)) { 886 | return 1; 887 | } 888 | if (Py_IsTrue(x)) { 889 | return 2; 890 | } 891 | if (Py_IsFalse(x)) { 892 | return 3; 893 | } 894 | return 0; 895 | } 896 | 897 | void test_py_is_not(PyObject *x) 898 | { 899 | if (!Py_IsNone(x)) { 900 | return 1; 901 | } 902 | if (!Py_IsTrue(x)) { 903 | return 2; 904 | } 905 | if (!Py_IsFalse(x)) { 906 | return 3; 907 | } 908 | return 0; 909 | } 910 | """) 911 | 912 | self.check_replace(""" 913 | void test_expr(struct MyStruct *obj, PyObject **obj2) 914 | { 915 | if (obj->attr1 == Py_None) { 916 | return 1; 917 | } 918 | if (obj->attr2.name == Py_None) { 919 | return 1; 920 | } 921 | if (*obj2 == Py_None) { 922 | return 1; 923 | } 924 | return 0; 925 | } 926 | """, """ 927 | #include "pythoncapi_compat.h" 928 | 929 | void test_expr(struct MyStruct *obj, PyObject **obj2) 930 | { 931 | if (Py_IsNone(obj->attr1)) { 932 | return 1; 933 | } 934 | if (Py_IsNone(obj->attr2.name)) { 935 | return 1; 936 | } 937 | if (Py_IsNone(*obj2)) { 938 | return 1; 939 | } 940 | return 0; 941 | } 942 | """) 943 | 944 | 945 | def test_no_compat(self): 946 | # Don't add "#include "pythoncapi_compat.h" 947 | source = """ 948 | void test_type(PyObject *obj, PyTypeObject *type) 949 | { 950 | obj->ob_type = type; 951 | } 952 | """ 953 | expected = """ 954 | void test_type(PyObject *obj, PyTypeObject *type) 955 | { 956 | Py_SET_TYPE(obj, type); 957 | } 958 | """ 959 | self.check_replace(source, expected, no_compat=True) 960 | 961 | if __name__ == "__main__": 962 | unittest.main() 963 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | 5 | PYTHON3 = (sys.version_info >= (3,)) 6 | 7 | 8 | def run_command(cmd, **kw): 9 | if hasattr(subprocess, 'run'): 10 | proc = subprocess.run(cmd, **kw) 11 | else: 12 | kw['shell'] = False 13 | proc = subprocess.Popen(cmd, **kw) 14 | try: 15 | proc.wait() 16 | except: 17 | proc.kill() 18 | proc.wait() 19 | raise 20 | 21 | exitcode = proc.returncode 22 | if exitcode: 23 | sys.exit(exitcode) 24 | 25 | 26 | def command_stdout(cmd, **kw): 27 | kw['stdout'] = subprocess.PIPE 28 | kw['universal_newlines'] = True 29 | if hasattr(subprocess, 'run'): 30 | proc = subprocess.run(cmd, **kw) 31 | return (proc.returncode, proc.stdout) 32 | else: 33 | kw['shell'] = False 34 | proc = subprocess.Popen(cmd, **kw) 35 | try: 36 | stdout = proc.communicate()[0] 37 | except: 38 | proc.kill() 39 | proc.wait() 40 | raise 41 | return (proc.returncode, stdout) 42 | -------------------------------------------------------------------------------- /upgrade_pythoncapi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import os 4 | import re 5 | import urllib.request 6 | import sys 7 | 8 | 9 | MIN_PYTHON = (2, 7) 10 | 11 | 12 | PYTHONCAPI_COMPAT_URL = ('https://raw.githubusercontent.com/python/' 13 | 'pythoncapi-compat/main/pythoncapi_compat.h') 14 | PYTHONCAPI_COMPAT_H = 'pythoncapi_compat.h' 15 | INCLUDE_PYTHONCAPI_COMPAT = f'#include "{PYTHONCAPI_COMPAT_H}"' 16 | INCLUDE_PYTHONCAPI_COMPAT2 = f'#include <{PYTHONCAPI_COMPAT_H}>' 17 | 18 | C_FILE_EXT = ( 19 | # C language 20 | ".c", ".h", 21 | # C++ language 22 | ".cc", ".cpp", ".cxx", ".hpp", 23 | ) 24 | IGNORE_DIRS = (".git", ".tox") 25 | 26 | 27 | # Match spaces but not newline characters. 28 | # Similar to \s but exclude newline characters and only look for ASCII spaces 29 | SPACE_REGEX = r'[ \t\f\v]' 30 | # Match the end of a line: newline characters of a single line 31 | NEWLINE_REGEX = r'(?:\n|\r|\r\n)' 32 | # Match the indentation at the beginning of a line 33 | INDENTATION_REGEX = fr'^{SPACE_REGEX}*' 34 | 35 | 36 | # Match a C identifier: 'identifier', 'var_3', 'NameCamelCase', '_var' 37 | # Use \b to only match a full word: match "a_b", but not just "b" in "a_b". 38 | ID_REGEX = r'\b[a-zA-Z_][a-zA-Z0-9_]*\b' 39 | # Match 'array[3]' 40 | SUBEXPR_REGEX = fr'{ID_REGEX}(?:\[[^]]+\])*' 41 | # Match a C expression like "frame", "frame.attr", "obj->attr" or "*obj". 42 | # Don't match functions calls like "func()". 43 | EXPR_REGEX = (fr"\*?" # "*" prefix 44 | fr"{SUBEXPR_REGEX}" # "var" 45 | fr"(?:(?:->|\.){SUBEXPR_REGEX})*") # "->attr" or ".attr" 46 | 47 | # # Match 'PyObject *var' and 'struct MyStruct* var' 48 | TYPE_PTR_REGEX = fr'{ID_REGEX} *\*' 49 | 50 | # Match '(PyObject*)' and nothing 51 | OPT_CAST_REGEX = fr'(?:\({TYPE_PTR_REGEX} *\){SPACE_REGEX}*)?' 52 | 53 | 54 | def same_indentation(group): 55 | # the regex must have re.MULTILINE flag 56 | return fr'{SPACE_REGEX}*(?:{NEWLINE_REGEX}{group})?' 57 | 58 | 59 | def get_member_regex_str(member): 60 | # Match "var->member". 61 | return fr'\b({EXPR_REGEX}) *-> *{member}\b' 62 | 63 | 64 | def get_member_regex(member): 65 | # Match "var->member" (get). 66 | # Don't match "var->member = value" (set). 67 | # Don't match "Py_CLEAR(var->member)". 68 | # Only "Py_CLEAR(" exact string is excluded. 69 | regex = (r'(?member = expr;". 82 | regex = assign_regex_str(get_member_regex_str(member), r'([^=].*)') 83 | return re.compile(regex) 84 | 85 | 86 | def call_assign_regex(name): 87 | # Match "Py_TYPE(expr) = expr;". 88 | # Don't match "assert(Py_TYPE(expr) == expr);". 89 | # Tolerate spaces 90 | regex = fr'{name} *\( *(.+) *\) *= *([^=].*) *;' 91 | return re.compile(regex) 92 | 93 | 94 | def is_c_filename(filename): 95 | return filename.endswith(C_FILE_EXT) 96 | 97 | 98 | class Operation: 99 | NAME = "" 100 | REPLACE = () 101 | NEED_PYTHONCAPI_COMPAT = False 102 | 103 | def __init__(self, patcher): 104 | self.patcher = patcher 105 | 106 | def patch(self, content): 107 | old_content = content 108 | for regex, replace in self.REPLACE: 109 | content = regex.sub(replace, content) 110 | if content != old_content and self.NEED_PYTHONCAPI_COMPAT: 111 | content = self.patcher.add_pythoncapi_compat(content) 112 | return content 113 | 114 | 115 | class Py_TYPE(Operation): 116 | NAME = "Py_TYPE" 117 | REPLACE = ( 118 | (get_member_regex('ob_type'), r'Py_TYPE(\1)'), 119 | ) 120 | # Py_TYPE() was added to Python 2.6. 121 | 122 | 123 | class Py_SIZE(Operation): 124 | NAME = "Py_SIZE" 125 | REPLACE = ( 126 | (get_member_regex('ob_size'), r'Py_SIZE(\1)'), 127 | ) 128 | # Py_SIZE() was added to Python 2.6. 129 | 130 | 131 | class Py_REFCNT(Operation): 132 | NAME = "Py_REFCNT" 133 | REPLACE = ( 134 | (get_member_regex('ob_refcnt'), r'Py_REFCNT(\1)'), 135 | ) 136 | # Py_REFCNT() was added to Python 2.6. 137 | 138 | 139 | class Py_SET_TYPE(Operation): 140 | NAME = "Py_SET_TYPE" 141 | REPLACE = ( 142 | (call_assign_regex('Py_TYPE'), r'Py_SET_TYPE(\1, \2);'), 143 | (set_member_regex('ob_type'), r'Py_SET_TYPE(\1, \2);'), 144 | ) 145 | # Need Py_SET_TYPE(): new in Python 3.9. 146 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 147 | 148 | 149 | class Py_SET_SIZE(Operation): 150 | NAME = "Py_SET_SIZE" 151 | REPLACE = ( 152 | (call_assign_regex('Py_SIZE'), r'Py_SET_SIZE(\1, \2);'), 153 | (set_member_regex('ob_size'), r'Py_SET_SIZE(\1, \2);'), 154 | ) 155 | # Need Py_SET_SIZE(): new in Python 3.9. 156 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 157 | 158 | 159 | class Py_SET_REFCNT(Operation): 160 | NAME = "Py_SET_REFCNT" 161 | REPLACE = ( 162 | (call_assign_regex('Py_REFCNT'), r'Py_SET_REFCNT(\1, \2);'), 163 | (set_member_regex('ob_refcnt'), r'Py_SET_REFCNT(\1, \2);'), 164 | ) 165 | # Need Py_SET_REFCNT(): new in Python 3.9. 166 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 167 | 168 | 169 | class PyObject_NEW(Operation): 170 | NAME = "PyObject_NEW" 171 | # In Python 3.9, the PyObject_NEW() macro becomes an alias to the 172 | # PyObject_New() macro, and the PyObject_NEW_VAR() macro becomes an alias 173 | # to the PyObject_NewVar() macro. 174 | REPLACE = ( 175 | (re.compile(r"\bPyObject_NEW\b( *\()"), r'PyObject_New\1'), 176 | (re.compile(r"\bPyObject_NEW_VAR\b( *\()"), r'PyObject_NewVar\1'), 177 | ) 178 | 179 | 180 | class PyMem_MALLOC(Operation): 181 | NAME = "PyMem_MALLOC" 182 | # In Python 3.9, the PyObject_NEW() macro becomes an alias to the 183 | # PyObject_New() macro, and the PyObject_NEW_VAR() macro becomes an alias 184 | # to the PyObject_NewVar() macro. 185 | 186 | REPLACE = ( 187 | (re.compile(r"\bPyMem_MALLOC\b( *\()"), r'PyMem_Malloc\1'), 188 | (re.compile(r"\bPyMem_REALLOC\b( *\()"), r'PyMem_Realloc\1'), 189 | (re.compile(r"\bPyMem_FREE\b( *\()"), r'PyMem_Free\1'), 190 | (re.compile(r"\bPyMem_Del\b( *\()"), r'PyMem_Free\1'), 191 | (re.compile(r"\bPyMem_DEL\b( *\()"), r'PyMem_Free\1'), 192 | ) 193 | 194 | 195 | class PyObject_MALLOC(Operation): 196 | NAME = "PyObject_MALLOC" 197 | # In Python 3.9, the PyObject_NEW() macro becomes an alias to the 198 | # PyObject_New() macro, and the PyObject_NEW_VAR() macro becomes an alias 199 | # to the PyObject_NewVar() macro. 200 | 201 | REPLACE = ( 202 | (re.compile(r"\bPyObject_MALLOC\b( *\()"), r'PyObject_Malloc\1'), 203 | (re.compile(r"\bPyObject_REALLOC\b( *\()"), r'PyObject_Realloc\1'), 204 | (re.compile(r"\bPyObject_FREE\b( *\()"), r'PyObject_Free\1'), 205 | (re.compile(r"\bPyObject_Del\b( *\()"), r'PyObject_Free\1'), 206 | (re.compile(r"\bPyObject_DEL\b( *\()"), r'PyObject_Free\1'), 207 | ) 208 | 209 | 210 | class PyFrame_GetBack(Operation): 211 | NAME = "PyFrame_GetBack" 212 | REPLACE = ( 213 | (get_member_regex('f_back'), r'_PyFrame_GetBackBorrow(\1)'), 214 | ) 215 | # Need _PyFrame_GetBackBorrow() (PyFrame_GetBack() is new in Python 3.9) 216 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 217 | 218 | 219 | class PyFrame_GetCode(Operation): 220 | NAME = "PyFrame_GetCode" 221 | 222 | REPLACE = ( 223 | (get_member_regex('f_code'), r'_PyFrame_GetCodeBorrow(\1)'), 224 | ) 225 | # Need _PyFrame_GetCodeBorrow() (PyFrame_GetCode() is new in Python 3.9) 226 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 227 | 228 | 229 | class PyThreadState_GetInterpreter(Operation): 230 | NAME = "PyThreadState_GetInterpreter" 231 | REPLACE = ( 232 | (get_member_regex('interp'), r'PyThreadState_GetInterpreter(\1)'), 233 | ) 234 | # Need PyThreadState_GetInterpreter() (new in Python 3.9) 235 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 236 | 237 | 238 | class PyThreadState_GetFrame(Operation): 239 | NAME = "PyThreadState_GetFrame" 240 | REPLACE = ( 241 | (get_member_regex('frame'), r'_PyThreadState_GetFrameBorrow(\1)'), 242 | ) 243 | # Need _PyThreadState_GetFrameBorrow() 244 | # (PyThreadState_GetFrame() is new in Python 3.9) 245 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 9)) 246 | 247 | 248 | class Py_NewRef(Operation): 249 | NAME = "Py_NewRef" 250 | REPLACE = ( 251 | # "Py_INCREF(x); return x;" => "return Py_NewRef(x);" 252 | # "Py_XINCREF(x); return x;" => "return Py_XNewRef(x);" 253 | # The two statements must be at the same indentation, otherwise the 254 | # regex does not match. 255 | (re.compile(fr'({INDENTATION_REGEX})' 256 | + fr'Py_(X?)INCREF\(({EXPR_REGEX})\)\s*;' 257 | + same_indentation(r'\1') 258 | + fr'return {OPT_CAST_REGEX}\3;', 259 | re.MULTILINE), 260 | r'\1return Py_\2NewRef(\3);'), 261 | 262 | # Same regex than the previous one, 263 | # but the two statements are on the same line. 264 | (re.compile(fr'Py_(X?)INCREF\(({EXPR_REGEX})\)\s*;' 265 | + fr'{SPACE_REGEX}*' 266 | + fr'return {OPT_CAST_REGEX}\2;', 267 | re.MULTILINE), 268 | r'return Py_\1NewRef(\2);'), 269 | 270 | # "Py_INCREF(x); y = x;" must be replaced before 271 | # "y = x; Py_INCREF(y);", to not miss consecutive 272 | # "Py_INCREF; assign; Py_INCREF; assign; ..." (see unit tests). 273 | 274 | # "Py_INCREF(x); y = x;" => "y = Py_NewRef(x)" 275 | # "Py_XINCREF(x); y = x;" => "y = Py_XNewRef(x)" 276 | # The two statements must have the same indentation, otherwise the 277 | # regex does not match. 278 | (re.compile(fr'({INDENTATION_REGEX})' 279 | + fr'Py_(X?)INCREF\(({EXPR_REGEX})\);' 280 | + same_indentation(r'\1') 281 | + assign_regex_str(fr'({EXPR_REGEX})', 282 | fr'{OPT_CAST_REGEX}\3'), 283 | re.MULTILINE), 284 | r'\1\4 = Py_\2NewRef(\3);'), 285 | 286 | # Same regex than the previous one, 287 | # but the two statements are on the same line. 288 | (re.compile(fr'Py_(X?)INCREF\(({EXPR_REGEX})\);' 289 | + fr'{SPACE_REGEX}*' 290 | + assign_regex_str(fr'({EXPR_REGEX})', 291 | fr'{OPT_CAST_REGEX}\2')), 292 | r'\3 = Py_\1NewRef(\2);'), 293 | 294 | # "y = x; Py_INCREF(x);" => "y = Py_NewRef(x);" 295 | # "y = x; Py_INCREF(y);" => "y = Py_NewRef(x);" 296 | # "y = x; Py_XINCREF(x);" => "y = Py_XNewRef(x);" 297 | # "y = x; Py_XINCREF(y);" => "y = Py_XNewRef(x);" 298 | # "y = (PyObject*)x; Py_XINCREF(y);" => "y = Py_XNewRef(x);" 299 | # The two statements must have the same indentation, otherwise the 300 | # regex does not match. 301 | (re.compile(fr'({INDENTATION_REGEX})' 302 | + assign_regex_str(fr'({EXPR_REGEX})', 303 | fr'{OPT_CAST_REGEX}({EXPR_REGEX})') 304 | + same_indentation(r'\1') 305 | + r'Py_(X?)INCREF\((?:\2|\3)\);', 306 | re.MULTILINE), 307 | r'\1\2 = Py_\4NewRef(\3);'), 308 | 309 | # Same regex than the previous one, 310 | # but the two statements are on the same line. 311 | (re.compile(assign_regex_str(fr'({EXPR_REGEX})', 312 | fr'{OPT_CAST_REGEX}({EXPR_REGEX})') 313 | + fr'{SPACE_REGEX}*' 314 | + r'Py_(X?)INCREF\((?:\1|\2)\);'), 315 | r'\1 = Py_\3NewRef(\2);'), 316 | 317 | # "PyObject *var = x; Py_INCREF(x);" => "PyObject *var = Py_NewRef(x);" 318 | # The two statements must have the same indentation, otherwise the 319 | # regex does not match. 320 | (re.compile(fr'({INDENTATION_REGEX})' 321 | # "type* var = expr;" 322 | + assign_regex_str(fr'({TYPE_PTR_REGEX} *)({EXPR_REGEX})', 323 | fr'({OPT_CAST_REGEX})({EXPR_REGEX})') 324 | + same_indentation(r'\1') 325 | # "Py_INCREF(var);" 326 | + r'Py_(X?)INCREF\((?:\3|\5)\);', 327 | re.MULTILINE), 328 | r'\1\2\3 = \4Py_\6NewRef(\5);'), 329 | 330 | # "Py_INCREF(x); PyObject *var = x;" => "PyObject *var = Py_NewRef(x);" 331 | # The two statements must have the same indentation, otherwise the 332 | # regex does not match. 333 | (re.compile(fr'({INDENTATION_REGEX})' 334 | # "Py_INCREF(var);" 335 | + fr'Py_(X?)INCREF\(({EXPR_REGEX})\);' 336 | + same_indentation(r'\1') 337 | # "type* var = expr;" 338 | + assign_regex_str(fr'({TYPE_PTR_REGEX} *{EXPR_REGEX})', 339 | fr'({OPT_CAST_REGEX})\3'), 340 | re.MULTILINE), 341 | r'\1\4 = \5Py_\2NewRef(\3);'), 342 | ) 343 | # Need Py_NewRef(): new in Python 3.10 344 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 10)) 345 | 346 | 347 | class Py_CLEAR(Operation): 348 | NAME = "Py_CLEAR" 349 | REPLACE = ( 350 | # "Py_XDECREF(x); x = NULL;" => "Py_CLEAR(x)"; 351 | # The two statements must have the same indentation, otherwise the 352 | # regex does not match. 353 | (re.compile(fr'({INDENTATION_REGEX})' 354 | + fr'Py_XDECREF\(({EXPR_REGEX})\) *;' 355 | + same_indentation(r'\1') 356 | + assign_regex_str(r'\2', r'NULL'), 357 | re.MULTILINE), 358 | r'\1Py_CLEAR(\2);'), 359 | 360 | # "Py_XDECREF(x); x = NULL;" => "Py_CLEAR(x)"; 361 | (re.compile(fr'Py_XDECREF\(({EXPR_REGEX})\) *;' 362 | + fr'{SPACE_REGEX}*' 363 | + assign_regex_str(r'\1', r'NULL')), 364 | r'Py_CLEAR(\1);'), 365 | ) 366 | 367 | 368 | SETREF_VALUE = fr'{OPT_CAST_REGEX}(?:{EXPR_REGEX}|Py_X?NewRef\({EXPR_REGEX}\))' 369 | 370 | 371 | class Py_SETREF(Operation): 372 | NAME = "Py_SETREF" 373 | REPLACE = ( 374 | # "Py_INCREF(y); Py_CLEAR(x); x = y;" => "Py_XSETREF(x, y)"; 375 | # Statements must have the same indentation, otherwise the regex does 376 | # not match. 377 | (re.compile(fr'({INDENTATION_REGEX})' 378 | + fr'Py_(X?)INCREF\(({EXPR_REGEX})\) *;' 379 | + same_indentation(r'\1') 380 | + fr'Py_CLEAR\(({EXPR_REGEX})\) *;' 381 | + same_indentation(r'\1') 382 | + assign_regex_str(r'\4', r'\3'), 383 | re.MULTILINE), 384 | r'\1Py_XSETREF(\4, Py_\2NewRef(\3));'), 385 | 386 | # "Py_CLEAR(x); x = y;" => "Py_XSETREF(x, y)"; 387 | # Statements must have the same indentation, otherwise the regex does 388 | # not match. 389 | (re.compile(fr'({INDENTATION_REGEX})' 390 | + fr'Py_CLEAR\(({EXPR_REGEX})\) *;' 391 | + same_indentation(r'\1') 392 | + assign_regex_str(r'\2', 393 | fr'({SETREF_VALUE})'), 394 | re.MULTILINE), 395 | r'\1Py_XSETREF(\2, \3);'), 396 | 397 | # "Py_INCREF(y); Py_DECREF(x); x = y;" => "Py_SETREF(x, y)"; 398 | # Statements must have the same indentation, otherwise the regex does 399 | # not match. 400 | (re.compile(fr'({INDENTATION_REGEX})' 401 | + fr'Py_(X?)INCREF\(({EXPR_REGEX})\) *;' 402 | + same_indentation(r'\1') 403 | + fr'Py_(X?)DECREF\(({EXPR_REGEX})\) *;' 404 | + same_indentation(r'\1') 405 | + assign_regex_str(r'\5', r'\3'), 406 | re.MULTILINE), 407 | r'\1Py_\4SETREF(\5, Py_\2NewRef(\3));'), 408 | 409 | # "Py_DECREF(x); x = y;" => "Py_SETREF(x, y)"; 410 | # "Py_DECREF(x); x = Py_NewRef(y);" => "Py_SETREF(x, Py_NewRef(y))"; 411 | # Statements must have the same indentation, otherwise the regex does 412 | # not match. 413 | (re.compile(fr'({INDENTATION_REGEX})' 414 | + fr'Py_(X?)DECREF\(({EXPR_REGEX})\) *;' 415 | + same_indentation(r'\1') 416 | + assign_regex_str(r'\3', 417 | fr'({SETREF_VALUE})'), 418 | re.MULTILINE), 419 | r'\1Py_\2SETREF(\3, \4);'), 420 | 421 | # "old = var; var = new; Py_DECREF(old);" => "Py_SETREF(var, new);" 422 | # "PyObject *old = var; var = new; Py_DECREF(old);" => "Py_SETREF(var, new);" 423 | # Statements must have the same indentation, otherwise the regex does 424 | # not match. 425 | (re.compile(fr'({INDENTATION_REGEX})' 426 | + fr'(?:{ID_REGEX} *\* *)?({ID_REGEX}) *= *{OPT_CAST_REGEX}({EXPR_REGEX}) *;' 427 | + same_indentation(r'\1') 428 | + assign_regex_str(r'\3', 429 | fr'({SETREF_VALUE})') 430 | + same_indentation(r'\1') 431 | + fr'Py_(X?)DECREF\(\2\) *;', 432 | re.MULTILINE), 433 | r'\1Py_\5SETREF(\3, \4);'), 434 | ) 435 | # Need Py_NewRef(): new in Python 3.5 436 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 5)) 437 | 438 | 439 | class Py_Is(Operation): 440 | NAME = "Py_Is" 441 | 442 | def replace2(regs): 443 | x = regs.group(1) 444 | y = regs.group(2) 445 | if y == 'NULL': 446 | return regs.group(0) 447 | return f'{x} = _Py_StealRef({y});' 448 | 449 | REPLACE = [] 450 | expr = fr'({EXPR_REGEX})' 451 | for name in ('None', 'True', 'False'): 452 | REPLACE.extend(( 453 | (re.compile(fr'{expr} == Py_{name}\b'), 454 | fr'Py_Is{name}(\1)'), 455 | (re.compile(fr'{expr} != Py_{name}\b'), 456 | fr'!Py_Is{name}(\1)'), 457 | )) 458 | 459 | # Need Py_IsNone(), Py_IsTrue(), Py_IsFalse(): new in Python 3.10 460 | NEED_PYTHONCAPI_COMPAT = (MIN_PYTHON < (3, 10)) 461 | 462 | 463 | OPERATIONS = ( 464 | Py_SET_TYPE, 465 | Py_SET_SIZE, 466 | Py_SET_REFCNT, 467 | # Py_SET_xxx must be run before Py_xxx 468 | Py_TYPE, 469 | Py_SIZE, 470 | Py_REFCNT, 471 | 472 | Py_Is, 473 | 474 | PyObject_NEW, 475 | PyMem_MALLOC, 476 | PyObject_MALLOC, 477 | 478 | PyFrame_GetBack, 479 | PyFrame_GetCode, 480 | 481 | PyThreadState_GetInterpreter, 482 | PyThreadState_GetFrame, 483 | 484 | # Code style: excluded from "all" 485 | Py_NewRef, 486 | Py_CLEAR, 487 | Py_SETREF, 488 | ) 489 | 490 | EXCLUDE_FROM_ALL = ( 491 | Py_NewRef, 492 | Py_CLEAR, 493 | Py_SETREF, 494 | ) 495 | 496 | 497 | def all_operations(): 498 | return set(operation_class.NAME for operation_class in OPERATIONS 499 | if operation_class not in EXCLUDE_FROM_ALL) 500 | 501 | 502 | class Patcher: 503 | def __init__(self, args=None): 504 | self.exitcode = 0 505 | self.pythoncapi_compat_added = 0 506 | self.want_pythoncapi_compat = False 507 | self.operations = None 508 | self.applied_operations = set() 509 | 510 | # Set temporariliy by patch() 511 | self._has_pythoncapi_compat = None 512 | self._applied_operations = None 513 | 514 | self._parse_options(args) 515 | 516 | def log(self, msg=''): 517 | print(msg, file=sys.stderr, flush=True) 518 | 519 | def warning(self, msg): 520 | self.log(f"WARNING: {msg}") 521 | 522 | def _get_operations(self, parser): 523 | args_names = self.args.operations.split(',') 524 | 525 | wanted = set() 526 | for name in args_names: 527 | name = name.strip() 528 | if not name: 529 | continue 530 | 531 | if name == "all": 532 | wanted |= all_operations() 533 | elif name.startswith("-"): 534 | name = name[1:] 535 | wanted.discard(name) 536 | else: 537 | wanted.add(name) 538 | 539 | operations = [] 540 | for operation_class in OPERATIONS: 541 | name = operation_class.NAME 542 | if name not in wanted: 543 | continue 544 | wanted.discard(name) 545 | operation = operation_class(self) 546 | operations.append(operation) 547 | 548 | if wanted: 549 | print(f"invalid operations: {','.join(wanted)}") 550 | print() 551 | self.usage(parser) 552 | sys.exit(1) 553 | 554 | return operations 555 | 556 | def add_line(self, content, line): 557 | # Use the first matching newline 558 | match = re.search(r'(?:\r\n|\n|\r)', content) 559 | newline = match.group(0) if match else '\n' 560 | 561 | line = line + newline 562 | # FIXME: tolerate trailing spaces 563 | if line not in content: 564 | # FIXME: add macro after the first header comment 565 | # FIXME: add macro after includes 566 | # FIXME: add macro after: #define PY_SSIZE_T_CLEAN 567 | return line + newline + content 568 | else: 569 | return content 570 | 571 | def add_pythoncapi_compat(self, content): 572 | if self._has_pythoncapi_compat: 573 | return content 574 | content = self.add_line(content, INCLUDE_PYTHONCAPI_COMPAT) 575 | self._has_pythoncapi_compat = True 576 | self.pythoncapi_compat_added += 1 577 | return content 578 | 579 | def _patch(self, content): 580 | try: 581 | has = (self.args.no_compat 582 | or INCLUDE_PYTHONCAPI_COMPAT in content 583 | or INCLUDE_PYTHONCAPI_COMPAT2 in content) 584 | self._has_pythoncapi_compat = has 585 | self._applied_operations = [] 586 | for operation in self.operations: 587 | new_content = operation.patch(content) 588 | if new_content != content: 589 | self._applied_operations.append(operation.NAME) 590 | content = new_content 591 | applied_operations = self._applied_operations 592 | finally: 593 | self._has_pythoncapi_compat = None 594 | self._applied_operations = None 595 | return (content, applied_operations) 596 | 597 | def patch(self, content): 598 | return self._patch(content)[0] 599 | 600 | def patch_file(self, filename): 601 | if os.path.basename(filename) == PYTHONCAPI_COMPAT_H: 602 | self.log(f"Skip {filename}") 603 | return 604 | 605 | encoding = "utf-8" 606 | errors = "surrogateescape" 607 | 608 | with open(filename, encoding=encoding, errors=errors, newline="") as fp: 609 | old_contents = fp.read() 610 | 611 | new_contents, operations = self._patch(old_contents) 612 | 613 | if self.args.to_stdout: 614 | print(new_contents, end="") 615 | return (new_contents != old_contents) 616 | 617 | # Don't rewrite if the filename for in-place replacement, 618 | # to avoid changing the file modification time. 619 | if new_contents == old_contents: 620 | return False 621 | 622 | if not self.args.no_backup: 623 | old_filename = filename + ".old" 624 | # If old_filename already exists, replace it 625 | os.replace(filename, old_filename) 626 | 627 | with open(filename, "w", encoding=encoding, errors=errors, newline="") as fp: 628 | fp.write(new_contents) 629 | 630 | self.applied_operations |= set(operations) 631 | operations = ', '.join(operations) 632 | self.log(f"Patched file: {filename} ({operations})") 633 | return True 634 | 635 | def _walk_dir(self, path): 636 | empty = True 637 | 638 | for dirpath, dirnames, filenames in os.walk(path): 639 | # Don't walk into .tox 640 | for ignore_name in IGNORE_DIRS: 641 | try: 642 | dirnames.remove(ignore_name) 643 | except ValueError: 644 | pass 645 | for filename in filenames: 646 | if is_c_filename(filename): 647 | yield os.path.join(dirpath, filename) 648 | empty = False 649 | 650 | if empty: 651 | self.warning(f"Directory {path} doesn't contain any C file") 652 | self.exitcode = 1 653 | 654 | def walk(self, paths): 655 | for path in paths: 656 | if os.path.isdir(path): 657 | for filename in self._walk_dir(path): 658 | yield filename 659 | elif os.path.exists(path): 660 | yield path 661 | else: 662 | self.warning(f"Path {path} does not exist") 663 | self.exitcode = 1 664 | 665 | def get_latest_header(self, base_dir): 666 | target = os.path.join(base_dir, PYTHONCAPI_COMPAT_H) 667 | self.log(f"Download the file from {PYTHONCAPI_COMPAT_URL} to {target}.") 668 | urllib.request.urlretrieve(PYTHONCAPI_COMPAT_URL, target) 669 | 670 | @staticmethod 671 | def usage(parser): 672 | parser.print_help() 673 | print() 674 | print("Operations:") 675 | print() 676 | for operation in sorted(OPERATIONS, 677 | key=lambda operation: operation.NAME.lower()): 678 | print(f"- {operation.NAME}") 679 | print() 680 | print("If a directory is passed, search for .c and .h files " 681 | "in subdirectories.") 682 | 683 | def _parse_dir_path(self, path): 684 | if os.path.isdir(path): 685 | return path 686 | else: 687 | raise argparse.ArgumentTypeError(f"{path} is not a valid path") 688 | 689 | def _parse_options(self, args): 690 | parser = argparse.ArgumentParser( 691 | description="Upgrade C extension modules to newer Python C API") 692 | parser.add_argument( 693 | '-o', '--operations', action="store", 694 | default="all", 695 | help='Space separated list of operation names to apply') 696 | parser.add_argument( 697 | '-q', '--quiet', action="store_true", 698 | help='Quiet mode') 699 | parser.add_argument( 700 | '-c', '--to-stdout', action="store_true", 701 | help='Write output into stdout instead of modifying files ' 702 | 'in-place (imply quiet mode)') 703 | parser.add_argument( 704 | '-B', '--no-backup', action="store_true", 705 | help="Don't create .old backup files") 706 | parser.add_argument( 707 | '-C', '--no-compat', action="store_true", 708 | help=f"Don't add: {INCLUDE_PYTHONCAPI_COMPAT}") 709 | parser.add_argument( 710 | '-d', '--download', metavar='PATH', 711 | help=f'Download latest pythoncapi_compat.h file to designated PATH', 712 | type=self._parse_dir_path) 713 | parser.add_argument( 714 | metavar='file_or_directory', dest="paths", nargs='*') 715 | 716 | args = parser.parse_args(args) 717 | if not args.paths and not args.download: 718 | self.usage(parser) 719 | sys.exit(1) 720 | 721 | if args.to_stdout: 722 | args.quiet = True 723 | 724 | self.args = args 725 | self.operations = self._get_operations(parser) 726 | 727 | def main(self): 728 | if self.args.paths: 729 | for filename in self.walk(self.args.paths): 730 | self.patch_file(filename) 731 | 732 | if self.applied_operations: 733 | nops = len(self.applied_operations) 734 | ops = ', '.join(sorted(self.applied_operations)) 735 | self.log() 736 | self.log(f"Applied operations ({nops}): {ops}") 737 | 738 | if self.args.download: 739 | path = self.args.download 740 | self.get_latest_header(path) 741 | 742 | if self.pythoncapi_compat_added and not self.args.quiet: 743 | self.log() 744 | self.log(f"{INCLUDE_PYTHONCAPI_COMPAT} added: you may have " 745 | f"to copy {PYTHONCAPI_COMPAT_H} to your project") 746 | self.log("Run 'python upgrade_pythoncapi.py --download '") 747 | 748 | sys.exit(self.exitcode) 749 | 750 | 751 | if __name__ == "__main__": 752 | Patcher().main() 753 | --------------------------------------------------------------------------------