├── .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 |
--------------------------------------------------------------------------------