├── tests ├── __init__.py ├── test_pythoncapi_compat_cppext.cpp ├── utils.py ├── setup.py ├── test_pythoncapi_compat.py ├── test_upgrade_pythoncapi.py └── test_pythoncapi_compat_cext.c ├── .gitignore ├── meson.build ├── .readthedocs.yaml ├── docs ├── tests.rst ├── Makefile ├── index.rst ├── make.bat ├── links.rst ├── conf.py ├── users.rst ├── upgrade.rst ├── changelog.rst └── api.rst ├── CODE_OF_CONDUCT.md ├── COPYING ├── README.rst ├── runtests.py ├── .github └── workflows │ └── build.yml └── upgrade_pythoncapi.py /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.swp 3 | tests/build/ 4 | *.py[cod] 5 | __pycache__ 6 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'pythoncapi-compat', 3 | 'c' 4 | ) 5 | 6 | incdir = include_directories('.') 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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-lts-latest 8 | tools: 9 | python: "3" 10 | 11 | sphinx: 12 | configuration: docs/conf.py 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | # Formatting checks 39 | '-Wformat', 40 | '-Wformat-nonliteral', 41 | '-Wformat-security', 42 | )) 43 | CFLAGS = COMMON_FLAGS + [ 44 | # Use C99 for pythoncapi_compat.c which initializes PyModuleDef with a 45 | # mixture of designated and non-designated initializers 46 | '-std=c99', 47 | ] 48 | else: 49 | # C compiler flags for MSVC 50 | COMMON_FLAGS.extend(( 51 | # Treat all compiler warnings as compiler errors 52 | '/WX', 53 | )) 54 | CFLAGS = list(COMMON_FLAGS) 55 | CXXFLAGS = list(COMMON_FLAGS) 56 | 57 | 58 | def main(): 59 | # gh-105776: When "gcc -std=11" is used as the C++ compiler, -std=c11 60 | # option emits a C++ compiler warning. Remove "-std11" option from the 61 | # CC command. 62 | cmd = (sysconfig.get_config_var('CC') or '') 63 | if cmd: 64 | cmd = shlex.split(cmd) 65 | cmd = [arg for arg in cmd if not arg.startswith('-std=')] 66 | if (sys.version_info >= (3, 8)): 67 | cmd = shlex.join(cmd) 68 | elif (sys.version_info >= (3, 3)): 69 | cmd = ' '.join(shlex.quote(arg) for arg in cmd) 70 | else: 71 | # Python 2.7 72 | import pipes 73 | cmd = ' '.join(pipes.quote(arg) for arg in cmd) 74 | # CC env var overrides sysconfig CC variable in setuptools 75 | os.environ['CC'] = cmd 76 | 77 | # C extension 78 | c_ext = Extension( 79 | 'test_pythoncapi_compat_cext', 80 | sources=['test_pythoncapi_compat_cext.c'], 81 | extra_compile_args=CFLAGS) 82 | extensions = [c_ext] 83 | 84 | if TEST_CXX: 85 | # C++ extension 86 | 87 | # MSVC has /std flag but doesn't support /std:c++11 88 | if not MSVC: 89 | versions = [ 90 | ('test_pythoncapi_compat_cpp03ext', ['-std=c++03']), 91 | ('test_pythoncapi_compat_cpp11ext', ['-std=c++11']), 92 | ] 93 | else: 94 | versions = [ 95 | ('test_pythoncapi_compat_cppext', None), 96 | ('test_pythoncapi_compat_cpp14ext', ['/std:c++14', '/Zc:__cplusplus']), 97 | ] 98 | for name, std_flags in versions: 99 | flags = list(CXXFLAGS) 100 | if std_flags is not None: 101 | flags.extend(std_flags) 102 | cpp_ext = Extension( 103 | name, 104 | sources=['test_pythoncapi_compat_cppext.cpp'], 105 | extra_compile_args=flags, 106 | language='c++') 107 | extensions.append(cpp_ext) 108 | 109 | setup(name="test_pythoncapi_compat", 110 | ext_modules=extensions) 111 | 112 | 113 | if __name__ == "__main__": 114 | main() 115 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | - "3.13t" 31 | # CPython 3.14 final is scheduled for October 2025: 32 | # https://peps.python.org/pep-0719/ 33 | - "3.14" 34 | - "3.14t" 35 | 36 | # PyPy versions: 37 | # - https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md 38 | # - https://downloads.python.org/pypy/ 39 | # - Only versions listed in this JSON are supported: 40 | # https://downloads.python.org/pypy/versions.json 41 | - "pypy2.7" 42 | - "pypy3.6" 43 | - "pypy3.7" 44 | - "pypy3.8" 45 | - "pypy3.9" 46 | - "pypy3.10" 47 | - "pypy3.11" 48 | 49 | # Old PyPy versions 50 | # See https://foss.heptapod.net/pypy/pypy/-/issues/3991 51 | - "pypy2.7-v7.3.2" 52 | - "pypy3.6-v7.3.2" 53 | - "pypy3.7-v7.3.2" 54 | 55 | include: 56 | # Windows 57 | - os: windows-latest 58 | python: "3.6" 59 | - os: windows-latest 60 | python: "3.7" 61 | - os: windows-latest 62 | python: "3.8" 63 | - os: windows-latest 64 | python: "3.9" 65 | - os: windows-latest 66 | python: "3.10" 67 | - os: windows-latest 68 | python: "3.11" 69 | - os: windows-latest 70 | python: "3.12" 71 | - os: windows-latest 72 | python: "3.13" 73 | - os: windows-latest 74 | python: "3.13t" 75 | 76 | # macOS 77 | # Python 3.9 is the oldest version available on macOS/arm64. 78 | - os: macos-latest 79 | python: "3.9" 80 | - os: macos-latest 81 | python: "3.10" 82 | - os: macos-latest 83 | python: "3.11" 84 | - os: macos-latest 85 | python: "3.12" 86 | - os: macos-latest 87 | python: "3.13" 88 | - os: macos-latest 89 | python: "3.13t" 90 | 91 | # Ubuntu: test deadsnakes Python versions which are not supported by 92 | # GHA python-versions. 93 | - os: ubuntu-22.04 94 | python: "3.7" 95 | 96 | steps: 97 | # https://github.com/actions/checkout 98 | - uses: actions/checkout@v4 99 | - name: Setup Python 100 | # https://github.com/actions/setup-python 101 | uses: actions/setup-python@v5 102 | with: 103 | python-version: ${{ matrix.python }} 104 | allow-prereleases: true 105 | - name: Install setuptools 106 | run: python -m pip install setuptools 107 | - name: Display the Python version 108 | run: python -VV 109 | - name: Run tests 110 | run: python runtests.py --current --verbose 111 | 112 | test_python27: 113 | # Get Python 2.7 from Ubuntu 22.04. 114 | # 115 | # Python 2.7 was removed from GHA setup-python in June 2023: 116 | # https://github.com/actions/setup-python/issues/672 117 | name: 'Test Python 2.7' 118 | runs-on: ubuntu-22.04 119 | steps: 120 | - uses: actions/checkout@v4 121 | - name: Install Python 2.7 122 | run: | 123 | sudo apt-get update 124 | sudo apt-get -yq install python2.7 python2.7-dev 125 | - name: Display the Python version 126 | run: python2.7 -VV 127 | - name: Run tests 128 | run: python2.7 runtests.py --current --verbose 129 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | * 2025-10-14: Add functions: 5 | 6 | * ``PyTuple_FromArray()`` 7 | * ``PyUnstable_Unicode_GET_CACHED_HASH()`` 8 | 9 | * 2025-09-18: Add PEP 782 functions: 10 | 11 | * ``PyBytesWriter_Create()`` 12 | * ``PyBytesWriter_Discard()`` 13 | * ``PyBytesWriter_Finish()`` 14 | * ``PyBytesWriter_FinishWithPointer()`` 15 | * ``PyBytesWriter_FinishWithSize()`` 16 | * ``PyBytesWriter_Format()`` 17 | * ``PyBytesWriter_GetData()`` 18 | * ``PyBytesWriter_GetSize()`` 19 | * ``PyBytesWriter_Grow()`` 20 | * ``PyBytesWriter_GrowAndUpdatePointer()`` 21 | * ``PyBytesWriter_Resize()`` 22 | * ``PyBytesWriter_WriteBytes()`` 23 | 24 | * 2025-09-01: Add ``PyUnstable_Object_IsUniquelyReferenced()`` function. 25 | * 2025-06-09: Add ``PyUnicodeWriter_WriteASCII()`` function. 26 | * 2025-06-03: Add functions: 27 | 28 | * ``PySys_GetAttr()`` 29 | * ``PySys_GetAttrString()`` 30 | * ``PySys_GetOptionalAttr()`` 31 | * ``PySys_GetOptionalAttrString()`` 32 | 33 | * 2025-01-19: Add ``PyConfig_Get()`` functions. 34 | * 2025-01-06: Add ``Py_fopen()`` and ``Py_fclose()`` functions. 35 | * 2024-12-16: Add ``structmember.h`` constants: 36 | 37 | * ``Py_T_BOOL`` 38 | * ``Py_T_BYTE`` 39 | * ``Py_T_CHAR`` 40 | * ``Py_T_DOUBLE`` 41 | * ``Py_T_FLOAT`` 42 | * ``Py_T_INT`` 43 | * ``Py_T_LONGLONG`` 44 | * ``Py_T_LONG`` 45 | * ``Py_T_OBJECT_EX`` 46 | * ``Py_T_PYSSIZET`` 47 | * ``Py_T_SHORT`` 48 | * ``Py_T_STRING_INPLACE`` 49 | * ``Py_T_STRING`` 50 | * ``Py_T_UBYTE`` 51 | * ``Py_T_UINT`` 52 | * ``Py_T_ULONGLONG`` 53 | * ``Py_T_ULONG`` 54 | * ``Py_T_USHORT`` 55 | * ``_Py_T_NONE`` 56 | * ``_Py_T_OBJECT`` 57 | * ``Py_AUDIT_READ`` 58 | * ``Py_READONLY`` 59 | * ``_Py_WRITE_RESTRICTED`` 60 | 61 | * 2024-12-13: Add functions and structs: 62 | 63 | * ``PyLongLayout`` 64 | * ``PyLong_GetNativeLayout()`` 65 | * ``PyLongExport`` 66 | * ``PyLong_Export()`` 67 | * ``PyLong_FreeExport()`` 68 | * ``PyLongWriter`` 69 | * ``PyLongWriter_Create()`` 70 | * ``PyLongWriter_Finish()`` 71 | * ``PyLongWriter_Discard()`` 72 | 73 | * 2024-11-12: Add functions: 74 | 75 | * ``PyLong_IsPositive()`` 76 | * ``PyLong_IsNegative()`` 77 | * ``PyLong_IsZero()`` 78 | 79 | * 2024-10-09: Add functions: 80 | 81 | * ``PyBytes_Join()`` 82 | * ``PyIter_NextItem()`` 83 | * ``PyLong_AsInt32()`` 84 | * ``PyLong_AsInt64()`` 85 | * ``PyLong_AsUInt32()`` 86 | * ``PyLong_AsUInt64()`` 87 | * ``PyLong_FromInt32()`` 88 | * ``PyLong_FromInt64()`` 89 | * ``PyLong_FromUInt32()`` 90 | * ``PyLong_FromUInt64()`` 91 | * ``PyUnicode_Equal()`` 92 | * ``Py_HashBuffer()`` 93 | 94 | * 2024-07-18: Add functions: 95 | 96 | * ``PyUnicodeWriter_Create()`` 97 | * ``PyUnicodeWriter_Discard()`` 98 | * ``PyUnicodeWriter_Finish()`` 99 | * ``PyUnicodeWriter_WriteChar()`` 100 | * ``PyUnicodeWriter_WriteUTF8()`` 101 | * ``PyUnicodeWriter_WriteStr()`` 102 | * ``PyUnicodeWriter_WriteRepr()`` 103 | * ``PyUnicodeWriter_WriteSubstring()`` 104 | * ``PyUnicodeWriter_WriteWideChar()`` 105 | * ``PyUnicodeWriter_Format()`` 106 | 107 | * 2024-06-03: Add ``PyLong_GetSign()``. 108 | * 2024-04-23: Drop Python 3.5 support. It cannot be tested anymore (pip fails). 109 | * 2024-04-02: Add ``PyDict_SetDefaultRef()`` function. 110 | * 2024-03-29: Add ``PyList_GetItemRef()`` function. 111 | * 2024-03-21: Add functions: 112 | 113 | * ``Py_GetConstant()`` 114 | * ``Py_GetConstantBorrowed()`` 115 | 116 | * 2024-03-09: Add hash constants: 117 | 118 | * ``PyHASH_BITS`` 119 | * ``PyHASH_IMAG`` 120 | * ``PyHASH_INF`` 121 | * ``PyHASH_MODULUS`` 122 | 123 | * 2024-02-20: Add PyTime API: 124 | 125 | * ``PyTime_t`` type 126 | * ``PyTime_MIN`` and ``PyTime_MAX`` constants 127 | * ``PyTime_AsSecondsDouble()`` 128 | * ``PyTime_Monotonic()`` 129 | * ``PyTime_PerfCounter()`` 130 | * ``PyTime_Time()`` 131 | 132 | * 2023-12-15: Add function ``Py_HashPointer()``. 133 | * 2023-11-14: Add functions: 134 | 135 | * ``PyDict_Pop()`` 136 | * ``PyDict_PopString()`` 137 | 138 | * 2023-11-13: Add functions: 139 | 140 | * ``PyList_Extend()`` 141 | * ``PyList_Clear()`` 142 | 143 | * 2023-10-04: Add functions: 144 | 145 | * ``PyUnicode_EqualToUTF8()`` 146 | * ``PyUnicode_EqualToUTF8AndSize()`` 147 | 148 | * 2023-10-03: Add functions: 149 | 150 | * ``PyObject_VisitManagedDict()`` 151 | * ``PyObject_ClearManagedDict()`` 152 | * ``PyThreadState_GetUnchecked()`` 153 | 154 | * 2023-09-29: Add functions: 155 | 156 | * ``PyMapping_HasKeyWithError()`` 157 | * ``PyMapping_HasKeyStringWithError()`` 158 | * ``PyObject_HasAttrWithError()`` 159 | * ``PyObject_HasAttrStringWithError()`` 160 | 161 | * 2023-08-25: Add ``PyDict_ContainsString()`` and ``PyLong_AsInt()`` functions. 162 | * 2023-08-21: Remove support for Python 2.7, Python 3.4 and older. 163 | * 2023-08-16: Add ``Py_IsFinalizing()`` function. 164 | * 2023-07-21: Add ``PyDict_GetItemRef()`` function. 165 | * 2023-07-18: Add ``PyModule_Add()`` function. 166 | * 2023-07-12: Add functions: 167 | 168 | * ``PyObject_GetOptionalAttr()`` 169 | * ``PyObject_GetOptionalAttrString()`` 170 | * ``PyMapping_GetOptionalItem()`` 171 | * ``PyMapping_GetOptionalItemString()`` 172 | 173 | * 2023-07-05: Add ``PyObject_Vectorcall()`` function. 174 | * 2023-06-21: Add ``PyWeakref_GetRef()`` function. 175 | * 2023-06-20: Add ``PyImport_AddModuleRef()`` function. 176 | * 2022-11-15: Add experimental operations to the ``upgrade_pythoncapi.py`` 177 | script: ``Py_NewRef``, ``Py_CLEAR`` and ``Py_SETREF``. 178 | * 2022-11-09: Fix ``Py_SETREF()`` and ``Py_XSETREF()`` macros 179 | for `gh-98724 `_. 180 | * 2022-11-04: Add ``PyFrame_GetVar()`` and ``PyFrame_GetVarString()`` 181 | functions. 182 | * 2022-08-04: Add ``PyCode_GetVarnames()``, ``PyCode_GetFreevars()`` 183 | and ``PyCode_GetCellvars()`` functions. 184 | * 2022-06-14: Fix compatibility with C++ older than C++11. 185 | * 2022-05-03: Add ``PyCode_GetCode()`` function. 186 | * 2022-04-26: Rename the project from ``pythoncapi_compat`` to 187 | ``pythoncapi-compat``: replace the underscore separator with a dash. 188 | * 2022-04-08: Add functions ``PyFrame_GetLocals()``, ``PyFrame_GetGlobals()`` 189 | ``PyFrame_GetBuiltins()``, and ``PyFrame_GetLasti()``. 190 | * 2022-03-12: Add functions ``PyFloat_Pack2()``, ``PyFloat_Pack4()``, 191 | ``PyFloat_Pack8()``, ``PyFloat_Unpack2()``, ``PyFloat_Unpack4()`` and 192 | ``PyFloat_Unpack8()``. 193 | * 2022-03-03: The project moved to https://github.com/python/pythoncapi-compat 194 | * 2022-02-11: The project license changes from the MIT license to the Zero 195 | Clause BSD (0BSD) license. Projects copying ``pythoncapi_compat.h`` no longer 196 | have to include the MIT license and the copyright notice. 197 | * 2022-02-08: Add documentation. 198 | * 2022-02-09: ``pythoncapi_compat.h`` now supports C++ on Python 3.6 and newer: 199 | use ``nullptr`` and ``reinterpret_cast`` cast on C++, and use ``NULL`` 200 | and ``(type)`` cast on C. 201 | * 2021-10-15: Add ``PyThreadState_EnterTracing()`` and 202 | ``PyThreadState_LeaveTracing()``. 203 | * 2021-04-09: Add ``Py_Is()``, ``Py_IsNone()``, ``Py_IsTrue()``, 204 | ``Py_IsFalse()`` functions. 205 | * 2021-04-01: 206 | 207 | * Add ``Py_SETREF()``, ``Py_XSETREF()`` and ``Py_UNUSED()``. 208 | * Add PyPy support. 209 | 210 | * 2021-01-27: Fix compatibility with Visual Studio 2008 for Python 2.7. 211 | * 2020-11-30: Creation of the ``upgrade_pythoncapi.py`` script. 212 | * 2020-06-04: Creation of the ``pythoncapi_compat.h`` header file. 213 | 214 | -------------------------------------------------------------------------------- /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 temporarily 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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:: PyBytesWriter* PyBytesWriter_Create(Py_ssize_t size) 33 | .. c:function:: void* PyBytesWriter_GetData(PyBytesWriter *writer) 34 | .. c:function:: Py_ssize_t PyBytesWriter_GetSize(PyBytesWriter *writer) 35 | .. c:function:: PyObject* PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size) 36 | .. c:function:: PyObject* PyBytesWriter_Finish(PyBytesWriter *writer) 37 | .. c:function:: PyObject* PyBytesWriter_FinishWithPointer(PyBytesWriter *writer, void *buf) 38 | .. c:function:: void PyBytesWriter_Discard(PyBytesWriter *writer) 39 | .. c:function:: int PyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size) 40 | .. c:function:: int PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t size) 41 | .. c:function:: void* PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, void *buf) 42 | .. c:function:: int PyBytesWriter_WriteBytes(PyBytesWriter *writer, const void *bytes, Py_ssize_t size) 43 | .. c:function:: int PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) 44 | 45 | See `PyBytesWriter documentation `__. 46 | 47 | .. c:function:: PyObject* PySys_GetAttr(const char *name) 48 | 49 | See `PySys_GetAttr() documentation `__. 50 | 51 | .. c:function:: PyObject* PySys_GetAttrString(const char *name) 52 | 53 | See `PySys_GetAttrString() documentation `__. 54 | 55 | .. c:function:: PyObject* PySys_GetOptionalAttr(const char *name) 56 | 57 | See `PySys_GetOptionalAttr() documentation `__. 58 | 59 | .. c:function:: PyObject* PySys_GetOptionalAttrString(const char *name) 60 | 61 | See `PySys_GetOptionalAttrString() documentation `__. 62 | 63 | .. c:function:: PyObject* PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) 64 | 65 | See `PyTuple_FromArray() documentation `__. 66 | 67 | .. c:function:: Py_hash_t PyUnstable_Unicode_GET_CACHED_HASH(PyObject *op) 68 | 69 | See `PyUnstable_Unicode_GET_CACHED_HASH() documentation `__. 70 | 71 | On PyPy, always returns ``-1``. 72 | 73 | 74 | Python 3.14 75 | ----------- 76 | 77 | .. c:struct:: PyLongLayout 78 | 79 | See `PyLongLayout documentation `__. 80 | 81 | .. c:function:: const PyLongLayout* PyLong_GetNativeLayout(void) 82 | 83 | See `PyLong_GetNativeLayout() documentation `__. 84 | 85 | .. c:struct:: PyLongExport 86 | 87 | See `PyLongExport documentation `__. 88 | 89 | .. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long) 90 | 91 | See `PyLong_Export() documentation `__. 92 | 93 | .. c:function:: void PyLong_FreeExport(PyLongExport *export_long) 94 | 95 | See `PyLong_FreeExport() documentation `__. 96 | 97 | .. c:struct:: PyLongWriter 98 | 99 | See `PyLongWriter documentation `__. 100 | 101 | .. c:function:: PyLongWriter* PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) 102 | 103 | See `PyLongWriter_Create() documentation `__. 104 | 105 | .. c:function:: PyObject* PyLongWriter_Finish(PyLongWriter *writer) 106 | 107 | See `PyLongWriter_Finish() documentation `__. 108 | 109 | .. c:function:: void PyLongWriter_Discard(PyLongWriter *writer) 110 | 111 | See `PyLongWriter_Discard() documentation `__. 112 | 113 | .. c:function:: int PyLong_IsPositive(PyObject *obj) 114 | 115 | See `PyLong_IsPositive() documentation `__. 116 | 117 | .. c:function:: int PyLong_IsNegative(PyObject *obj) 118 | 119 | See `PyLong_IsNegative() documentation `__. 120 | 121 | .. c:function:: int PyLong_IsZero(PyObject *obj) 122 | 123 | See `PyLong_IsZero() documentation `__. 124 | 125 | .. c:function:: int PyLong_GetSign(PyObject *obj, int *sign) 126 | 127 | See `PyLong_GetSign() documentation `__. 128 | 129 | .. c:function:: PyObject* PyIter_NextItem(PyObject *sep, PyObject *iterable) 130 | 131 | See `PyIter_NextItem() documentation `__. 132 | 133 | .. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) 134 | 135 | See `PyBytes_Join() documentation `__. 136 | 137 | .. c:function:: Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) 138 | 139 | See `Py_HashBuffer() documentation `__. 140 | 141 | .. c:function:: int PyUnicode_Equal(PyObject *str1, PyObject *str2) 142 | 143 | See `PyUnicode_Equal() documentation `__. 144 | 145 | .. c:function:: PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) 146 | 147 | See `PyUnicodeWriter_Create() documentation `__. 148 | 149 | .. c:function:: PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) 150 | 151 | See `PyUnicodeWriter_Finish() documentation `__. 152 | 153 | .. c:function:: void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) 154 | 155 | See `PyUnicodeWriter_Discard() documentation `__. 156 | 157 | .. c:function:: int PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) 158 | 159 | See `PyUnicodeWriter_WriteChar() documentation `__. 160 | 161 | .. c:function:: int PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, const char *str, Py_ssize_t size) 162 | 163 | See `PyUnicodeWriter_WriteUTF8() documentation `__. 164 | 165 | .. c:function:: int PyUnicodeWriter_WriteASCII(PyUnicodeWriter *writer, const char *str, Py_ssize_t size) 166 | 167 | See `PyUnicodeWriter_WriteASCII() documentation `__. 168 | 169 | .. c:function:: int PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, const wchar_t *str, Py_ssize_t size) 170 | 171 | See `PyUnicodeWriter_WriteWideChar() documentation `__. 172 | 173 | .. c:function:: int PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) 174 | 175 | See `PyUnicodeWriter_WriteStr() documentation `__. 176 | 177 | .. c:function:: int PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) 178 | 179 | See `PyUnicodeWriter_WriteRepr() documentation `__. 180 | 181 | .. c:function:: int PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, Py_ssize_t start, Py_ssize_t end) 182 | 183 | See `PyUnicodeWriter_WriteSubstring() documentation `__. 184 | 185 | .. c:function:: int PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) 186 | 187 | See `PyUnicodeWriter_Format() documentation `__. 188 | 189 | .. c:function:: int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) 190 | 191 | See `PyLong_AsInt32() documentation `__. 192 | 193 | .. c:function:: int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) 194 | 195 | See `PyLong_AsInt64() documentation `__. 196 | 197 | .. c:function:: int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) 198 | 199 | See `PyLong_AsUInt32() documentation `__. 200 | 201 | .. c:function:: int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) 202 | 203 | See `PyLong_AsUInt64() documentation `__. 204 | 205 | .. c:function:: PyObject* PyLong_FromInt32(int32_t value) 206 | 207 | See `PyLong_FromInt32() documentation `__. 208 | 209 | .. c:function:: PyObject* PyLong_FromInt64(int64_t value) 210 | 211 | See `PyLong_FromInt64() documentation `__. 212 | 213 | .. c:function:: PyObject* PyLong_FromUInt32(uint32_t value) 214 | 215 | See `PyLong_FromUInt32() documentation `__. 216 | 217 | .. c:function:: PyObject* PyLong_FromUInt64(uint64_t value) 218 | 219 | See `PyLong_FromUInt64() documentation `__. 220 | 221 | .. c:function:: FILE* Py_fopen(PyObject *path, const char *mode) 222 | 223 | See `Py_fopen() documentation `__. 224 | 225 | .. c:function:: int Py_fclose(FILE *file) 226 | 227 | See `Py_fclose() documentation `__. 228 | 229 | .. c:function:: PyObject* PyConfig_Get(const char *name) 230 | 231 | See `PyConfig_Get() documentation `__. 232 | 233 | .. c:function:: int PyConfig_GetInt(const char *name, int *value) 234 | 235 | See `PyConfig_GetInt() documentation `__. 236 | 237 | .. c:function:: int PyUnstable_Object_IsUniquelyReferenced(PyObject *op) 238 | 239 | See `PyUnstable_Object_IsUniquelyReferenced() documentation `__. 240 | 241 | Not supported: 242 | 243 | * ``PyConfig_Names()`` 244 | * ``PyConfig_Set()`` 245 | * ``PyInitConfig_AddModule()`` 246 | * ``PyInitConfig_Create()`` 247 | * ``PyInitConfig_Free()`` 248 | * ``PyInitConfig_FreeStrList()`` 249 | * ``PyInitConfig_GetError()`` 250 | * ``PyInitConfig_GetExitCode()`` 251 | * ``PyInitConfig_GetInt()`` 252 | * ``PyInitConfig_GetStr()`` 253 | * ``PyInitConfig_GetStrList()`` 254 | * ``PyInitConfig_HasOption()`` 255 | * ``PyInitConfig_SetInt()`` 256 | * ``PyInitConfig_SetStr()`` 257 | * ``PyInitConfig_SetStrList()`` 258 | * ``PyType_GetBaseByToken()`` 259 | * ``PyUnicodeWriter_DecodeUTF8Stateful()`` 260 | * ``Py_InitializeFromInitConfig()`` 261 | 262 | 263 | Python 3.13 264 | ----------- 265 | 266 | .. c:function:: int PyDict_GetItemRef(PyObject *p, PyObject *key, PyObject **result) 267 | 268 | See `PyDict_GetItemRef() documentation `__. 269 | 270 | .. c:function:: int PyDict_GetItemStringRef(PyObject *p, const char *key, PyObject **result) 271 | 272 | See `PyDict_GetItemStringRef() documentation `__. 273 | 274 | .. c:function:: PyObject* PyImport_AddModuleRef(const char *name) 275 | 276 | See `PyImport_AddModuleRef() documentation `__. 277 | 278 | .. c:function:: int PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) 279 | 280 | See `PyObject_GetOptionalAttr() documentation `__. 281 | 282 | .. c:function:: int PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) 283 | 284 | See `PyObject_GetOptionalAttrString() documentation `__. 285 | 286 | .. c:function:: int PyObject_HasAttrWithError(PyObject *obj, PyObject *attr_name) 287 | 288 | See `PyObject_HasAttrWithError() documentation `__. 289 | 290 | .. c:function:: int PyObject_HasAttrStringWithError(PyObject *obj, const char *attr_name) 291 | 292 | See `PyObject_HasAttrStringWithError() documentation `__. 293 | 294 | .. c:function:: int PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) 295 | 296 | See `PyMapping_GetOptionalItem() documentation `__. 297 | 298 | .. c:function:: int PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) 299 | 300 | See `PyMapping_GetOptionalItemString() documentation `__. 301 | 302 | .. c:function:: int PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) 303 | 304 | See `PyMapping_HasKeyWithError() documentation `__. 305 | 306 | .. c:function:: int PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) 307 | 308 | See `PyMapping_HasKeyStringWithError() documentation `__. 309 | 310 | .. c:function:: int PyModule_Add(PyObject *module, const char *name, PyObject *value) 311 | 312 | See `PyModule_Add() documentation `__. 313 | 314 | .. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) 315 | 316 | See `PyWeakref_GetRef() documentation `__. 317 | 318 | .. c:function:: int Py_IsFinalizing() 319 | 320 | Return non-zero if the Python interpreter is shutting down, return 0 321 | otherwise. 322 | 323 | Availability: Python 3.3 and newer, PyPy 7.3 and newer. 324 | 325 | See `Py_IsFinalizing() documentation `__. 326 | 327 | .. c:function:: int PyDict_ContainsString(PyObject *p, const char *key) 328 | 329 | See `PyDict_ContainsString() documentation `__. 330 | 331 | .. c:function:: int PyLong_AsInt(PyObject *obj) 332 | 333 | See `PyLong_AsInt() documentation `__. 334 | 335 | .. c:function:: int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) 336 | 337 | See `PyObject_VisitManagedDict() documentation `__. 338 | 339 | .. c:function:: void PyObject_ClearManagedDict(PyObject *obj) 340 | 341 | See `PyObject_ClearManagedDict() documentation `__. 342 | 343 | .. c:function:: PyThreadState* PyThreadState_GetUnchecked(void) 344 | 345 | See `PyThreadState_GetUnchecked() documentation `__. 346 | 347 | Available on Python 3.5.2 and newer. 348 | 349 | .. c:function:: int PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) 350 | 351 | See `PyUnicode_EqualToUTF8() documentation `__. 352 | 353 | .. c:function:: int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t size) 354 | 355 | See `PyUnicode_EqualToUTF8AndSize() documentation `__. 356 | 357 | .. c:function:: int PyList_Extend(PyObject *list, PyObject *iterable) 358 | 359 | See `PyList_Extend() documentation `__. 360 | 361 | .. c:function:: int PyList_Clear(PyObject *list) 362 | 363 | See `PyList_Clear() documentation `__. 364 | 365 | .. c:function:: int PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) 366 | 367 | See `PyDict_Pop() documentation `__. 368 | 369 | .. c:function:: int PyDict_PopString(PyObject *dict, const char *key, PyObject **result) 370 | 371 | See `PyDict_PopString() documentation `__. 372 | 373 | .. c:function:: Py_hash_t Py_HashPointer(const void *ptr) 374 | 375 | See `Py_HashPointer() documentation `__. 376 | 377 | .. c:type:: PyTime_t 378 | 379 | A timestamp or duration in nanoseconds, represented as a signed 64-bit 380 | integer. 381 | 382 | .. c:var:: PyTime_t PyTime_MIN 383 | 384 | Minimum value of :c:type:`PyTime_t`. 385 | 386 | .. c:var:: PyTime_t PyTime_MAX 387 | 388 | Maximum value of :c:type:`PyTime_t`. 389 | 390 | .. c:function:: double PyTime_AsSecondsDouble(PyTime_t t) 391 | 392 | See `PyTime_AsSecondsDouble() documentation `__. 393 | 394 | .. c:function:: int PyTime_Monotonic(PyTime_t *result) 395 | 396 | See `PyTime_Monotonic() documentation `__. 397 | 398 | .. c:function:: int PyTime_Time(PyTime_t *result) 399 | 400 | See `PyTime_Time() documentation `__. 401 | 402 | .. c:function:: int PyTime_PerfCounter(PyTime_t *result) 403 | 404 | See `PyTime_PerfCounter() documentation `__. 405 | 406 | .. c:function:: PyObject* PyList_GetItemRef(PyObject *op, Py_ssize_t index) 407 | 408 | See `PyList_GetItemRef() documentation `__. 409 | 410 | .. c:function:: int PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, PyObject **result) 411 | 412 | See `PyDict_SetDefaultRef() documentation `__. 413 | 414 | 415 | Not supported: 416 | 417 | * ``PyErr_FormatUnraisable()``. 418 | * ``PyLong_AsNativeBytes()`` 419 | * ``PyLong_FromNativeBytes()`` 420 | * ``PyLong_FromUnsignedNativeBytes()`` 421 | * ``PyObject_GenericHash()``. 422 | * ``PySys_Audit()``. 423 | * ``PySys_AuditTuple()``. 424 | * ``PyType_GetFullyQualifiedName()`` 425 | * ``PyType_GetModuleName()`` 426 | 427 | Python 3.12 428 | ----------- 429 | 430 | .. c:function:: PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) 431 | 432 | See `PyFrame_GetVar() documentation `__. 433 | 434 | Not available on PyPy. 435 | 436 | .. c:function:: PyObject* PyFrame_GetVarString(PyFrameObject *frame, const char *name) 437 | 438 | See `PyFrame_GetVarString() documentation `__. 439 | 440 | Not available on PyPy. 441 | 442 | .. c:function:: PyObject* Py_GetConstant(unsigned int constant_id) 443 | 444 | See `Py_GetConstant() documentation `__. 445 | 446 | .. c:function:: PyObject* Py_GetConstantBorrowed(unsigned int constant_id) 447 | 448 | See `Py_GetConstantBorrowed() documentation `__. 449 | 450 | 451 | Not supported: 452 | 453 | * ``PyDict_AddWatcher()``, ``PyDict_Watch()``. 454 | * ``PyCode_AddWatcher()``, ``PyCode_ClearWatcher()``. 455 | * ``PyErr_GetRaisedException()``, ``PyErr_SetRaisedException()``. 456 | * ``_PyErr_ChainExceptions1()``. 457 | * ``PyErr_DisplayException()``. 458 | * ``_Py_IsImmortal()``. 459 | * ``Py_NewInterpreterFromConfig()``. 460 | * ``PyException_GetArgs()``, ``PyException_SetArgs()``. 461 | * ``PyEval_SetProfileAllThreads()``, ``PyEval_SetTraceAllThreads()``. 462 | * ``PyFunction_SetVectorcall()``. 463 | * ``PyType_FromMetaclass()``: implementation too big to be backported. 464 | * ``PyVectorcall_Call()``. 465 | 466 | Python 3.11 467 | ----------- 468 | 469 | .. c:function:: PyObject* PyCode_GetCellvars(PyCodeObject *code) 470 | 471 | See `PyCode_GetCellvars() documentation `__. 472 | 473 | Not available on PyPy. 474 | 475 | .. c:function:: PyObject* PyCode_GetCode(PyCodeObject *code) 476 | 477 | See `PyCode_GetCode() documentation `__. 478 | 479 | Not available on PyPy. 480 | 481 | .. c:function:: PyObject* PyCode_GetFreevars(PyCodeObject *code) 482 | 483 | See `PyCode_GetFreevars() documentation `__. 484 | 485 | Not available on PyPy. 486 | 487 | .. c:function:: PyObject* PyCode_GetVarnames(PyCodeObject *code) 488 | 489 | See `PyCode_GetVarnames() documentation `__. 490 | 491 | Not available on PyPy. 492 | 493 | .. c:function:: PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) 494 | 495 | See `PyFrame_GetBuiltins() documentation `__. 496 | 497 | Not available on PyPy. 498 | 499 | .. c:function:: PyObject* PyFrame_GetGlobals(PyFrameObject *frame) 500 | 501 | See `PyFrame_GetGlobals() documentation `__. 502 | 503 | Not available on PyPy. 504 | 505 | .. c:function:: int PyFrame_GetLasti(PyFrameObject *frame) 506 | 507 | See `PyFrame_GetLasti() documentation `__. 508 | 509 | Not available on PyPy. 510 | 511 | .. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame) 512 | 513 | See `PyFrame_GetLocals() documentation `__. 514 | 515 | Not available on PyPy. 516 | 517 | .. c:function:: void PyThreadState_EnterTracing(PyThreadState *tstate) 518 | 519 | See `PyThreadState_EnterTracing() documentation `__. 520 | 521 | Not available on PyPy. 522 | 523 | .. c:function:: void PyThreadState_LeaveTracing(PyThreadState *tstate) 524 | 525 | See `PyThreadState_LeaveTracing() documentation `__. 526 | 527 | Not available on PyPy 528 | 529 | .. c:function:: int PyFloat_Pack2(double x, unsigned char *p, int le) 530 | 531 | Pack a C double as the IEEE 754 binary16 half-precision format. 532 | 533 | Availability: Python 3.6 and newer. Not available on PyPy 534 | 535 | .. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le) 536 | 537 | Pack a C double as the IEEE 754 binary32 single precision format. 538 | 539 | Not available on PyPy 540 | 541 | .. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le) 542 | 543 | Pack a C double as the IEEE 754 binary64 double precision format. 544 | 545 | Not available on PyPy 546 | 547 | .. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le) 548 | 549 | Unpack the IEEE 754 binary16 half-precision format as a C double. 550 | 551 | Availability: Python 3.6 and newer. Not available on PyPy 552 | 553 | .. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le) 554 | 555 | Unpack the IEEE 754 binary32 single precision format as a C double. 556 | 557 | Not available on PyPy 558 | 559 | .. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le) 560 | 561 | Unpack the IEEE 754 binary64 double precision format as a C double. 562 | 563 | Not available on PyPy 564 | 565 | Not supported: 566 | 567 | * ``PyType_GetModuleByDef()``. 568 | * ``PyType_GetName()``. 569 | * ``PyType_GetQualName()``. 570 | * ``Py_Version`` constant. 571 | * ``PyErr_GetHandledException()``, ``PyErr_SetHandledException()``. 572 | * ``PyFrame_GetGenerator()``. 573 | 574 | Python 3.10 575 | ----------- 576 | 577 | .. c:function:: PyObject* Py_NewRef(PyObject *obj) 578 | 579 | See `Py_NewRef() documentation `__. 580 | 581 | .. c:function:: PyObject* Py_XNewRef(PyObject *obj) 582 | 583 | See `Py_XNewRef() documentation `__. 584 | 585 | .. c:function:: int Py_Is(PyObject *x, PyObject *y) 586 | 587 | See `Py_Is() documentation `__. 588 | 589 | .. c:function:: int Py_IsNone(PyObject *x) 590 | 591 | See `Py_IsNone() documentation `__. 592 | 593 | .. c:function:: int Py_IsTrue(PyObject *x) 594 | 595 | See `Py_IsTrue() documentation `__. 596 | 597 | .. c:function:: int Py_IsFalse(PyObject *x) 598 | 599 | See `Py_IsFalse() documentation `__. 600 | 601 | .. c:function:: int PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) 602 | 603 | See `PyModule_AddObjectRef() documentation `__. 604 | 605 | Not supported: 606 | 607 | * ``PyCodec_Unregister()``. 608 | * ``PyDateTime_DATE_GET_TZINFO()``, ``PyDateTime_TIME_GET_TZINFO()``. 609 | * ``PyErr_SetInterruptEx()``. 610 | * ``PyGC_Enable()``, ``PyGC_Disable()`` and ``PyGC_IsEnabled()``. 611 | * ``PyIter_Send()``. 612 | * ``PySet_CheckExact()``. 613 | * ``Py_TPFLAGS_DISALLOW_INSTANTIATION`` constant. 614 | * ``Py_TPFLAGS_IMMUTABLETYPE`` constant. 615 | 616 | Python 3.9 617 | ---------- 618 | 619 | PyObject 620 | ^^^^^^^^ 621 | 622 | .. c:function:: void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) 623 | 624 | See `Py_SET_REFCNT() documentation `__. 625 | 626 | .. c:function:: void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) 627 | 628 | See `Py_SET_TYPE() documentation `__. 629 | 630 | .. c:function:: void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) 631 | 632 | See `Py_SET_SIZE() documentation `__. 633 | 634 | .. c:function:: int Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) 635 | 636 | See `Py_IS_TYPE() documentation `__. 637 | 638 | .. c:function:: PyObject* PyObject_CallNoArgs(PyObject *func) 639 | 640 | See `PyObject_CallNoArgs() documentation `__. 641 | 642 | .. c:function:: PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) 643 | 644 | See `PyObject_CallOneArg() documentation `__. 645 | 646 | .. c:function:: PyObject* PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) 647 | 648 | See `PyObject_Vectorcall() documentation `__. 649 | 650 | .. c:function:: Py_ssize_t PyVectorcall_NARGS(size_t nargsf) 651 | 652 | See `PyVectorcall_NARGS() documentation `__. 653 | 654 | .. c:macro:: PY_VECTORCALL_ARGUMENTS_OFFSET 655 | 656 | See `PY_VECTORCALL_ARGUMENTS_OFFSET documentation `__. 657 | 658 | Not supported: 659 | 660 | * ``PyVectorcall_CallMethod()``. 661 | * ``PyType_FromModuleAndSpec()`` 662 | 663 | 664 | 665 | PyFrameObject 666 | ^^^^^^^^^^^^^ 667 | 668 | .. c:function:: PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) 669 | 670 | See `PyFrame_GetCode() documentation `__. 671 | 672 | .. c:function:: PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) 673 | 674 | See `PyFrame_GetBack() documentation `__. 675 | 676 | Not available on PyPy 677 | 678 | 679 | PyThreadState 680 | ^^^^^^^^^^^^^ 681 | 682 | .. c:function:: PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) 683 | 684 | See `PyThreadState_GetFrame() documentation `__. 685 | 686 | Not available on PyPy 687 | 688 | .. c:function:: PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState *tstate) 689 | 690 | See `PyThreadState_GetInterpreter() documentation `__. 691 | 692 | .. c:function:: uint64_t PyThreadState_GetID(PyThreadState *tstate) 693 | 694 | See `PyThreadState_GetID() documentation `__. 695 | 696 | Availability: Python 3.7. Not available on PyPy. 697 | 698 | PyInterpreterState 699 | ^^^^^^^^^^^^^^^^^^ 700 | 701 | .. c:function:: PyInterpreterState* PyInterpreterState_Get(void) 702 | 703 | See `PyInterpreterState_Get() documentation `__. 704 | 705 | 706 | GC protocol 707 | ^^^^^^^^^^^ 708 | 709 | .. c:function:: int PyObject_GC_IsTracked(PyObject* obj) 710 | 711 | See `PyObject_GC_IsTracked() documentation `__. 712 | 713 | Not available on PyPy. 714 | 715 | .. c:function:: int PyObject_GC_IsFinalized(PyObject *obj) 716 | 717 | See `PyObject_GC_IsFinalized() documentation `__. 718 | 719 | Availability: Python 3.4. Not available on PyPy. 720 | 721 | Module helper 722 | ^^^^^^^^^^^^^ 723 | 724 | .. c:function:: int PyModule_AddType(PyObject *module, PyTypeObject *type) 725 | 726 | See `PyModule_AddType() documentation `__. 727 | 728 | Python 3.8 729 | ---------- 730 | 731 | Not supported: 732 | 733 | * ``PyCode_NewWithPosOnlyArgs()``. 734 | 735 | Python 3.7 736 | ---------- 737 | 738 | Not supported: 739 | 740 | * ``PyImport_GetModule()``. 741 | * ``PyInterpreterState_GetID()``. 742 | * ``PySlice_Unpack()``, ``PySlice_AdjustIndices()``. 743 | * ``PyTimeZone_FromOffset()``, ``PyTimeZone_FromOffsetAndName()``. 744 | * ``Py_RETURN_RICHCOMPARE()``. 745 | * ``Py_UNREACHABLE`` macro. 746 | 747 | Python 3.6 748 | ---------- 749 | 750 | Not supported: 751 | 752 | * ``PyErr_ResourceWarning()``. 753 | * ``PyErr_SetImportErrorSubclass()``. 754 | * ``PyOS_FSPath()``. 755 | * ``Py_FinalizeEx()``. 756 | 757 | Python 3.5.2 758 | ------------ 759 | 760 | .. c:macro:: Py_SETREF(op, op2) 761 | 762 | .. c:macro:: Py_XSETREF(op, op2) 763 | 764 | Not supported: 765 | 766 | * ``PyCodec_NameReplaceErrors()``. 767 | * ``PyErr_FormatV()``. 768 | * ``PyExc_RecursionError``. 769 | * ``PyModule_FromDefAndSpec()``, ``PyModule_FromDefAndSpec2()``, 770 | and ``PyModule_ExecDef()``. 771 | * ``PyNumber_MatrixMultiply()`` and ``PyNumber_InPlaceMatrixMultiply()``. 772 | 773 | Python 3.4 774 | ---------- 775 | 776 | .. c:macro:: Py_UNUSED(name) 777 | 778 | See `Py_UNUSED() documentation `__. 779 | 780 | Python 3.2 781 | ---------- 782 | 783 | Not supported: 784 | 785 | * ``Py_VA_COPY``. 786 | * ``PySys_SetArgvEx()``. 787 | * ``PyLong_AsLongLongAndOverflow()``. 788 | * ``PyErr_NewExceptionWithDoc()``. 789 | 790 | Python 3.1 791 | ---------- 792 | 793 | Not supported: 794 | 795 | * ``PyOS_string_to_double()``. 796 | * ``PyCapsule`` API. 797 | 798 | Borrow variant 799 | -------------- 800 | 801 | To ease migration of C extensions to the new C API, a variant is provided 802 | to return borrowed references rather than strong references. 803 | 804 | These functions are only available in ``pythoncapi_compat.h`` and are not 805 | part of the Python C API. 806 | 807 | .. c:function:: PyObject* _Py_StealRef(PyObject *ob) 808 | 809 | Similar to ``Py_DECREF(ob); return ob;``. 810 | 811 | .. c:function:: PyObject* _Py_XStealRef(PyObject *ob) 812 | 813 | Similar to ``Py_XDECREF(ob); return ob;``. 814 | 815 | .. c:function:: PyFrameObject* _PyThreadState_GetFrameBorrow(PyThreadState *tstate) 816 | 817 | :c:func:`PyThreadState_GetFrame` variant. Not available on PyPy. 818 | 819 | .. c:function:: PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) 820 | 821 | :c:func:`PyFrame_GetCode` variant. 822 | 823 | .. c:function:: PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) 824 | 825 | :c:func:`PyFrame_GetBack` variant Not available on PyPy. 826 | 827 | For example, ``tstate->frame`` can be replaced with 828 | ``_PyThreadState_GetFrameBorrow(tstate)`` to avoid accessing directly 829 | ``PyThreadState.frame`` member. 830 | -------------------------------------------------------------------------------- /tests/test_pythoncapi_compat_cext.c: -------------------------------------------------------------------------------- 1 | // Always enable assertions 2 | #undef NDEBUG 3 | 4 | #include "pythoncapi_compat.h" 5 | #include // T_SHORT, READONLY 6 | 7 | #ifdef NDEBUG 8 | # error "assertions must be enabled" 9 | #endif 10 | 11 | #ifdef Py_LIMITED_API 12 | # error "Py_LIMITED_API is not supported" 13 | #endif 14 | 15 | #if PY_VERSION_HEX >= 0x03000000 16 | # define PYTHON3 1 17 | #endif 18 | 19 | #if defined(__cplusplus) && __cplusplus >= 201402 20 | # define MODULE_NAME test_pythoncapi_compat_cpp14ext 21 | #elif defined(__cplusplus) && __cplusplus >= 201103 22 | # define MODULE_NAME test_pythoncapi_compat_cpp11ext 23 | #elif defined(__cplusplus) && !defined(_MSC_VER) 24 | # define MODULE_NAME test_pythoncapi_compat_cpp03ext 25 | #elif defined(__cplusplus) 26 | # define MODULE_NAME test_pythoncapi_compat_cppext 27 | #else 28 | # define MODULE_NAME test_pythoncapi_compat_cext 29 | #endif 30 | 31 | #define _STR(NAME) #NAME 32 | #define STR(NAME) _STR(NAME) 33 | #define _CONCAT(a, b) a ## b 34 | #define CONCAT(a, b) _CONCAT(a, b) 35 | 36 | #define MODULE_NAME_STR STR(MODULE_NAME) 37 | 38 | // Ignore reference count checks on PyPy 39 | #ifndef PYPY_VERSION 40 | # define CHECK_REFCNT 41 | #endif 42 | 43 | // CPython 3.12 beta 1 implements immortal objects (PEP 683) 44 | #if 0x030C00B1 <= PY_VERSION_HEX && !defined(PYPY_VERSION) 45 | // Don't check reference count of Python 3.12 immortal objects (ex: bool 46 | // and str types) 47 | # define IMMORTAL_OBJS 48 | #endif 49 | 50 | #ifdef CHECK_REFCNT 51 | # define ASSERT_REFCNT(expr) assert(expr) 52 | #else 53 | # define ASSERT_REFCNT(expr) 54 | #endif 55 | 56 | // Marker to check that pointer value was set 57 | static const char uninitialized[] = "uninitialized"; 58 | #define UNINITIALIZED_OBJ ((PyObject *)uninitialized) 59 | #define UNINITIALIZED_INT 0x83ff979 60 | 61 | 62 | static PyObject* 63 | create_string(const char *str) 64 | { 65 | PyObject *obj; 66 | #ifdef PYTHON3 67 | obj = PyUnicode_FromString(str); 68 | #else 69 | obj = PyString_FromString(str); 70 | #endif 71 | assert(obj != _Py_NULL); 72 | return obj; 73 | } 74 | 75 | 76 | static PyObject * 77 | test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 78 | { 79 | PyObject *obj = PyList_New(0); 80 | if (obj == _Py_NULL) { 81 | return _Py_NULL; 82 | } 83 | Py_ssize_t refcnt = Py_REFCNT(obj); 84 | 85 | // Py_NewRef() 86 | PyObject *ref = Py_NewRef(obj); 87 | assert(ref == obj); 88 | assert(Py_REFCNT(obj) == (refcnt + 1)); 89 | Py_DECREF(ref); 90 | 91 | // Py_XNewRef() 92 | PyObject *xref = Py_XNewRef(obj); 93 | assert(xref == obj); 94 | assert(Py_REFCNT(obj) == (refcnt + 1)); 95 | Py_DECREF(xref); 96 | 97 | assert(Py_XNewRef(_Py_NULL) == _Py_NULL); 98 | 99 | // Py_SETREF() 100 | PyObject *setref = Py_NewRef(obj); 101 | PyObject *none = Py_None; 102 | assert(Py_REFCNT(obj) == (refcnt + 1)); 103 | 104 | Py_SETREF(setref, none); 105 | assert(setref == none); 106 | assert(Py_REFCNT(obj) == refcnt); 107 | Py_INCREF(setref); 108 | 109 | Py_SETREF(setref, _Py_NULL); 110 | assert(setref == _Py_NULL); 111 | 112 | // Py_XSETREF() 113 | PyObject *xsetref = _Py_NULL; 114 | 115 | Py_INCREF(obj); 116 | assert(Py_REFCNT(obj) == (refcnt + 1)); 117 | Py_XSETREF(xsetref, obj); 118 | assert(xsetref == obj); 119 | 120 | Py_XSETREF(xsetref, _Py_NULL); 121 | assert(Py_REFCNT(obj) == refcnt); 122 | assert(xsetref == _Py_NULL); 123 | 124 | // Py_SET_REFCNT 125 | Py_SET_REFCNT(obj, Py_REFCNT(obj)); 126 | // Py_SET_TYPE 127 | Py_SET_TYPE(obj, Py_TYPE(obj)); 128 | // Py_SET_SIZE 129 | Py_SET_SIZE(obj, Py_SIZE(obj)); 130 | // Py_IS_TYPE() 131 | int is_type = Py_IS_TYPE(obj, Py_TYPE(obj)); 132 | assert(is_type); 133 | 134 | assert(Py_REFCNT(obj) == refcnt); 135 | Py_DECREF(obj); 136 | Py_RETURN_NONE; 137 | } 138 | 139 | 140 | static PyObject * 141 | test_py_is(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 142 | { 143 | PyObject *o_none = Py_None; 144 | PyObject *o_true = Py_True; 145 | PyObject *o_false = Py_False; 146 | PyObject *obj = PyList_New(0); 147 | if (obj == _Py_NULL) { 148 | return _Py_NULL; 149 | } 150 | 151 | /* test Py_Is() */ 152 | assert(Py_Is(obj, obj)); 153 | assert(!Py_Is(obj, o_none)); 154 | 155 | /* test Py_IsNone() */ 156 | assert(Py_IsNone(o_none)); 157 | assert(!Py_IsNone(obj)); 158 | 159 | /* test Py_IsTrue() */ 160 | assert(Py_IsTrue(o_true)); 161 | assert(!Py_IsTrue(o_false)); 162 | assert(!Py_IsTrue(obj)); 163 | 164 | /* testPy_IsFalse() */ 165 | assert(Py_IsFalse(o_false)); 166 | assert(!Py_IsFalse(o_true)); 167 | assert(!Py_IsFalse(obj)); 168 | 169 | Py_DECREF(obj); 170 | Py_RETURN_NONE; 171 | } 172 | 173 | 174 | #ifndef PYPY_VERSION 175 | static void 176 | test_frame_getvar(PyFrameObject *frame) 177 | { 178 | // Make the assumption that test_frame_getvar() is only called by 179 | // _run_tests() of test_pythoncapi_compat.py and so that the "name" 180 | // variable exists. 181 | 182 | // test PyFrame_GetVar() and PyFrame_GetVarString() 183 | PyObject *attr = PyUnicode_FromString("name"); 184 | assert(attr != _Py_NULL); 185 | PyObject *name1 = PyFrame_GetVar(frame, attr); 186 | Py_DECREF(attr); 187 | assert(name1 != _Py_NULL); 188 | Py_DECREF(name1); 189 | 190 | PyObject *name2 = PyFrame_GetVarString(frame, "name"); 191 | assert(name2 != _Py_NULL); 192 | Py_DECREF(name2); 193 | 194 | // test PyFrame_GetVar() and PyFrame_GetVarString() NameError 195 | PyObject *attr3 = PyUnicode_FromString("dontexist"); 196 | assert(attr3 != _Py_NULL); 197 | PyObject *name3 = PyFrame_GetVar(frame, attr3); 198 | Py_DECREF(attr3); 199 | assert(name3 == _Py_NULL); 200 | assert(PyErr_ExceptionMatches(PyExc_NameError)); 201 | PyErr_Clear(); 202 | 203 | PyObject *name4 = PyFrame_GetVarString(frame, "dontexist"); 204 | assert(name4 == _Py_NULL); 205 | assert(PyErr_ExceptionMatches(PyExc_NameError)); 206 | PyErr_Clear(); 207 | } 208 | 209 | 210 | static PyObject * 211 | test_frame(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 212 | { 213 | PyThreadState *tstate = PyThreadState_Get(); 214 | 215 | // test PyThreadState_GetFrame() 216 | PyFrameObject *frame = PyThreadState_GetFrame(tstate); 217 | if (frame == _Py_NULL) { 218 | PyErr_SetString(PyExc_AssertionError, "PyThreadState_GetFrame failed"); 219 | return _Py_NULL; 220 | } 221 | 222 | // test _PyThreadState_GetFrameBorrow() 223 | Py_ssize_t frame_refcnt = Py_REFCNT(frame); 224 | PyFrameObject *frame2 = _PyThreadState_GetFrameBorrow(tstate); 225 | assert(frame2 == frame); 226 | assert(Py_REFCNT(frame) == frame_refcnt); 227 | 228 | // test PyFrame_GetCode() 229 | PyCodeObject *code = PyFrame_GetCode(frame); 230 | assert(code != _Py_NULL); 231 | assert(PyCode_Check(code)); 232 | 233 | // test _PyFrame_GetCodeBorrow() 234 | Py_ssize_t code_refcnt = Py_REFCNT(code); 235 | PyCodeObject *code2 = _PyFrame_GetCodeBorrow(frame); 236 | assert(code2 == code); 237 | assert(Py_REFCNT(code) == code_refcnt); 238 | Py_DECREF(code); 239 | 240 | // PyFrame_GetBack() 241 | PyFrameObject* back = PyFrame_GetBack(frame); 242 | if (back != _Py_NULL) { 243 | assert(PyFrame_Check(back)); 244 | } 245 | 246 | // test _PyFrame_GetBackBorrow() 247 | if (back != _Py_NULL) { 248 | Py_ssize_t back_refcnt = Py_REFCNT(back); 249 | PyFrameObject *back2 = _PyFrame_GetBackBorrow(frame); 250 | assert(back2 == back); 251 | assert(Py_REFCNT(back) == back_refcnt); 252 | } 253 | else { 254 | PyFrameObject *back2 = _PyFrame_GetBackBorrow(frame); 255 | assert(back2 == back); 256 | } 257 | Py_XDECREF(back); 258 | 259 | // test PyFrame_GetLocals() 260 | PyObject *locals = PyFrame_GetLocals(frame); 261 | assert(locals != _Py_NULL); 262 | // Python 3.13 creates a local proxy 263 | #if PY_VERSION_HEX < 0x030D0000 264 | assert(PyDict_Check(locals)); 265 | #endif 266 | 267 | // test PyFrame_GetGlobals() 268 | PyObject *globals = PyFrame_GetGlobals(frame); 269 | assert(globals != _Py_NULL); 270 | assert(PyDict_Check(globals)); 271 | 272 | // test PyFrame_GetBuiltins() 273 | PyObject *builtins = PyFrame_GetBuiltins(frame); 274 | assert(builtins != _Py_NULL); 275 | assert(PyDict_Check(builtins)); 276 | 277 | assert(locals != globals); 278 | assert(globals != builtins); 279 | assert(builtins != locals); 280 | 281 | Py_DECREF(locals); 282 | Py_DECREF(globals); 283 | Py_DECREF(builtins); 284 | 285 | // test PyFrame_GetLasti() 286 | int lasti = PyFrame_GetLasti(frame); 287 | assert(lasti >= 0); 288 | 289 | // test PyFrame_GetVar() and PyFrame_GetVarString() 290 | test_frame_getvar(frame); 291 | 292 | // done 293 | Py_DECREF(frame); 294 | Py_RETURN_NONE; 295 | } 296 | #endif 297 | 298 | 299 | static PyObject * 300 | test_thread_state(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 301 | { 302 | PyThreadState *tstate = PyThreadState_Get(); 303 | 304 | // test PyThreadState_GetInterpreter() 305 | PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate); 306 | assert(interp != _Py_NULL); 307 | 308 | #ifndef PYPY_VERSION 309 | // test PyThreadState_GetFrame() 310 | PyFrameObject *frame = PyThreadState_GetFrame(tstate); 311 | if (frame != _Py_NULL) { 312 | assert(PyFrame_Check(frame)); 313 | } 314 | Py_XDECREF(frame); 315 | #endif 316 | 317 | #if 0x030700A1 <= PY_VERSION_HEX && !defined(PYPY_VERSION) 318 | uint64_t id = PyThreadState_GetID(tstate); 319 | assert(id > 0); 320 | #endif 321 | 322 | #ifndef PYPY_VERSION 323 | // PyThreadState_EnterTracing(), PyThreadState_LeaveTracing() 324 | PyThreadState_EnterTracing(tstate); 325 | PyThreadState_LeaveTracing(tstate); 326 | #endif 327 | 328 | #if PY_VERSION_HEX >= 0x03050200 329 | // PyThreadState_GetUnchecked() 330 | assert(PyThreadState_GetUnchecked() == tstate); 331 | #endif 332 | 333 | Py_RETURN_NONE; 334 | } 335 | 336 | 337 | static PyObject * 338 | test_interpreter(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 339 | { 340 | // test PyInterpreterState_Get() 341 | PyInterpreterState *interp = PyInterpreterState_Get(); 342 | assert(interp != _Py_NULL); 343 | PyThreadState *tstate = PyThreadState_Get(); 344 | PyInterpreterState *interp2 = PyThreadState_GetInterpreter(tstate); 345 | assert(interp == interp2); 346 | 347 | #if 0x030300A1 <= PY_VERSION_HEX && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) 348 | // test Py_IsFinalizing() 349 | assert(Py_IsFinalizing() == 0); 350 | #endif 351 | 352 | Py_RETURN_NONE; 353 | } 354 | 355 | 356 | static PyObject * 357 | test_calls(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 358 | { 359 | PyObject *func = _Py_CAST(PyObject*, &PyUnicode_Type); 360 | 361 | // test PyObject_CallNoArgs(): str() returns '' 362 | PyObject *res = PyObject_CallNoArgs(func); 363 | if (res == _Py_NULL) { 364 | return _Py_NULL; 365 | } 366 | assert(PyUnicode_Check(res)); 367 | Py_DECREF(res); 368 | 369 | // test PyObject_CallOneArg(): str(1) returns '1' 370 | PyObject *arg = PyLong_FromLong(1); 371 | if (arg == _Py_NULL) { 372 | return _Py_NULL; 373 | } 374 | res = PyObject_CallOneArg(func, arg); 375 | Py_DECREF(arg); 376 | if (res == _Py_NULL) { 377 | return _Py_NULL; 378 | } 379 | assert(PyUnicode_Check(res)); 380 | Py_DECREF(res); 381 | 382 | Py_RETURN_NONE; 383 | } 384 | 385 | 386 | static PyObject * 387 | test_gc(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 388 | { 389 | PyObject *tuple = PyTuple_New(1); 390 | Py_INCREF(Py_None); 391 | PyTuple_SET_ITEM(tuple, 0, Py_None); 392 | 393 | #if !defined(PYPY_VERSION) 394 | // test PyObject_GC_IsTracked() 395 | int tracked = PyObject_GC_IsTracked(tuple); 396 | assert(tracked); 397 | #endif 398 | 399 | #if PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) 400 | // test PyObject_GC_IsFinalized() 401 | int finalized = PyObject_GC_IsFinalized(tuple); 402 | assert(!finalized); 403 | #endif 404 | 405 | Py_DECREF(tuple); 406 | Py_RETURN_NONE; 407 | } 408 | 409 | 410 | static int 411 | check_module_attr(PyObject *module, const char *name, PyObject *expected) 412 | { 413 | PyObject *attr = PyObject_GetAttrString(module, name); 414 | if (attr == _Py_NULL) { 415 | return -1; 416 | } 417 | assert(attr == expected); 418 | Py_DECREF(attr); 419 | 420 | if (PyObject_DelAttrString(module, name) < 0) { 421 | return -1; 422 | } 423 | return 0; 424 | } 425 | 426 | 427 | // test PyModule_AddType() 428 | static int 429 | test_module_add_type(PyObject *module) 430 | { 431 | PyTypeObject *type = &PyUnicode_Type; 432 | #ifdef PYTHON3 433 | const char *type_name = "str"; 434 | #else 435 | const char *type_name = "unicode"; 436 | #endif 437 | #ifdef CHECK_REFCNT 438 | Py_ssize_t refcnt = Py_REFCNT(type); 439 | #endif 440 | 441 | if (PyModule_AddType(module, type) < 0) { 442 | return -1; 443 | } 444 | #ifndef IMMORTAL_OBJS 445 | ASSERT_REFCNT(Py_REFCNT(type) == refcnt + 1); 446 | #endif 447 | 448 | if (check_module_attr(module, type_name, _Py_CAST(PyObject*, type)) < 0) { 449 | return -1; 450 | } 451 | ASSERT_REFCNT(Py_REFCNT(type) == refcnt); 452 | return 0; 453 | } 454 | 455 | 456 | // test PyModule_AddObjectRef() 457 | static int 458 | test_module_addobjectref(PyObject *module) 459 | { 460 | const char *name = "test_module_addobjectref"; 461 | PyObject *obj = PyUnicode_FromString(name); 462 | assert(obj != _Py_NULL); 463 | #ifdef CHECK_REFCNT 464 | Py_ssize_t refcnt = Py_REFCNT(obj); 465 | #endif 466 | 467 | if (PyModule_AddObjectRef(module, name, obj) < 0) { 468 | ASSERT_REFCNT(Py_REFCNT(obj) == refcnt); 469 | Py_DECREF(obj); 470 | return -1; 471 | } 472 | ASSERT_REFCNT(Py_REFCNT(obj) == refcnt + 1); 473 | 474 | if (check_module_attr(module, name, obj) < 0) { 475 | Py_DECREF(obj); 476 | return -1; 477 | } 478 | ASSERT_REFCNT(Py_REFCNT(obj) == refcnt); 479 | 480 | // PyModule_AddObjectRef() with value=NULL must not crash 481 | assert(!PyErr_Occurred()); 482 | int res = PyModule_AddObjectRef(module, name, _Py_NULL); 483 | assert(res < 0); 484 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 485 | PyErr_Clear(); 486 | 487 | Py_DECREF(obj); 488 | return 0; 489 | } 490 | 491 | 492 | // test PyModule_Add() 493 | static int 494 | test_module_add(PyObject *module) 495 | { 496 | const char *name = "test_module_add"; 497 | PyObject *obj = PyUnicode_FromString(name); 498 | assert(obj != _Py_NULL); 499 | #ifdef CHECK_REFCNT 500 | Py_ssize_t refcnt = Py_REFCNT(obj); 501 | #endif 502 | 503 | if (PyModule_Add(module, name, Py_NewRef(obj)) < 0) { 504 | ASSERT_REFCNT(Py_REFCNT(obj) == refcnt); 505 | Py_DECREF(obj); 506 | return -1; 507 | } 508 | ASSERT_REFCNT(Py_REFCNT(obj) == refcnt + 1); 509 | 510 | if (check_module_attr(module, name, obj) < 0) { 511 | Py_DECREF(obj); 512 | return -1; 513 | } 514 | ASSERT_REFCNT(Py_REFCNT(obj) == refcnt); 515 | 516 | // PyModule_Add() with value=NULL must not crash 517 | assert(!PyErr_Occurred()); 518 | int res = PyModule_Add(module, name, _Py_NULL); 519 | assert(res < 0); 520 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 521 | PyErr_Clear(); 522 | 523 | Py_DECREF(obj); 524 | return 0; 525 | } 526 | 527 | 528 | static PyObject * 529 | test_module(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 530 | { 531 | PyObject *module = PyImport_ImportModule("sys"); 532 | if (module == _Py_NULL) { 533 | return _Py_NULL; 534 | } 535 | assert(PyModule_Check(module)); 536 | 537 | if (test_module_add_type(module) < 0) { 538 | goto error; 539 | } 540 | if (test_module_addobjectref(module) < 0) { 541 | goto error; 542 | } 543 | if (test_module_add(module) < 0) { 544 | goto error; 545 | } 546 | 547 | Py_DECREF(module); 548 | Py_RETURN_NONE; 549 | 550 | error: 551 | Py_DECREF(module); 552 | return _Py_NULL; 553 | } 554 | 555 | 556 | #if (PY_VERSION_HEX <= 0x030B00A1 || 0x030B00A7 <= PY_VERSION_HEX) && !defined(PYPY_VERSION) 557 | static PyObject * 558 | test_float_pack(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 559 | { 560 | const int big_endian = 0; 561 | const int little_endian = 1; 562 | char data[8]; 563 | const double d = 1.5; 564 | 565 | #if PY_VERSION_HEX >= 0x030600B1 566 | # define HAVE_FLOAT_PACK2 567 | #endif 568 | 569 | // Test Pack (big endian) 570 | #ifdef HAVE_FLOAT_PACK2 571 | assert(PyFloat_Pack2(d, data, big_endian) == 0); 572 | assert(memcmp(data, ">\x00", 2) == 0); 573 | #endif 574 | 575 | assert(PyFloat_Pack4(d, data, big_endian) == 0); 576 | assert(memcmp(data, "?\xc0\x00\x00", 2) == 0); 577 | 578 | assert(PyFloat_Pack8(d, data, big_endian) == 0); 579 | assert(memcmp(data, "?\xf8\x00\x00\x00\x00\x00\x00", 2) == 0); 580 | 581 | // Test Pack (little endian) 582 | #ifdef HAVE_FLOAT_PACK2 583 | assert(PyFloat_Pack2(d, data, little_endian) == 0); 584 | assert(memcmp(data, "\x00>", 2) == 0); 585 | #endif 586 | 587 | assert(PyFloat_Pack4(d, data, little_endian) == 0); 588 | assert(memcmp(data, "\x00\x00\xc0?", 2) == 0); 589 | 590 | assert(PyFloat_Pack8(d, data, little_endian) == 0); 591 | assert(memcmp(data, "\x00\x00\x00\x00\x00\x00\xf8?", 2) == 0); 592 | 593 | // Test Unpack (big endian) 594 | #ifdef HAVE_FLOAT_PACK2 595 | assert(PyFloat_Unpack2(">\x00", big_endian) == d); 596 | #endif 597 | assert(PyFloat_Unpack4("?\xc0\x00\x00", big_endian) == d); 598 | assert(PyFloat_Unpack8("?\xf8\x00\x00\x00\x00\x00\x00", big_endian) == d); 599 | 600 | // Test Unpack (little endian) 601 | #ifdef HAVE_FLOAT_PACK2 602 | assert(PyFloat_Unpack2("\x00>", little_endian) == d); 603 | #endif 604 | assert(PyFloat_Unpack4("\x00\x00\xc0?", little_endian) == d); 605 | assert(PyFloat_Unpack8("\x00\x00\x00\x00\x00\x00\xf8?", little_endian) == d); 606 | 607 | Py_RETURN_NONE; 608 | } 609 | #endif 610 | 611 | 612 | #if !defined(PYPY_VERSION) 613 | static PyObject * 614 | test_code(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) 615 | { 616 | PyThreadState *tstate = PyThreadState_Get(); 617 | PyFrameObject *frame = PyThreadState_GetFrame(tstate); 618 | if (frame == _Py_NULL) { 619 | PyErr_SetString(PyExc_AssertionError, "PyThreadState_GetFrame failed"); 620 | return _Py_NULL; 621 | } 622 | PyCodeObject *code = PyFrame_GetCode(frame); 623 | 624 | // PyCode_GetCode() 625 | { 626 | PyObject *co_code = PyCode_GetCode(code); 627 | assert(co_code != _Py_NULL); 628 | assert(PyBytes_Check(co_code)); 629 | Py_DECREF(co_code); 630 | } 631 | 632 | // PyCode_GetVarnames 633 | { 634 | PyObject *co_varnames = PyCode_GetVarnames(code); 635 | assert(co_varnames != _Py_NULL); 636 | assert(PyTuple_CheckExact(co_varnames)); 637 | assert(PyTuple_GET_SIZE(co_varnames) != 0); 638 | Py_DECREF(co_varnames); 639 | } 640 | 641 | // PyCode_GetCellvars 642 | { 643 | PyObject *co_cellvars = PyCode_GetCellvars(code); 644 | assert(co_cellvars != _Py_NULL); 645 | assert(PyTuple_CheckExact(co_cellvars)); 646 | assert(PyTuple_GET_SIZE(co_cellvars) == 0); 647 | Py_DECREF(co_cellvars); 648 | } 649 | 650 | // PyCode_GetFreevars 651 | { 652 | PyObject *co_freevars = PyCode_GetFreevars(code); 653 | assert(co_freevars != _Py_NULL); 654 | assert(PyTuple_CheckExact(co_freevars)); 655 | assert(PyTuple_GET_SIZE(co_freevars) == 0); 656 | Py_DECREF(co_freevars); 657 | } 658 | 659 | Py_DECREF(code); 660 | Py_DECREF(frame); 661 | Py_RETURN_NONE; 662 | } 663 | #endif 664 | 665 | 666 | #ifdef __cplusplus 667 | // Class to test operator casting an object to PyObject* 668 | class StrongRef 669 | { 670 | public: 671 | StrongRef(PyObject *obj) : m_obj(obj) { 672 | Py_INCREF(this->m_obj); 673 | } 674 | 675 | ~StrongRef() { 676 | Py_DECREF(this->m_obj); 677 | } 678 | 679 | // Cast to PyObject*: get a borrowed reference 680 | inline operator PyObject*() const { return this->m_obj; } 681 | 682 | private: 683 | PyObject *m_obj; // Strong reference 684 | }; 685 | #endif 686 | 687 | 688 | static PyObject * 689 | test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 690 | { 691 | PyObject *obj = Py_BuildValue("(ii)", 1, 2); 692 | if (obj == _Py_NULL) { 693 | return _Py_NULL; 694 | } 695 | Py_ssize_t refcnt = Py_REFCNT(obj); 696 | assert(refcnt >= 1); 697 | 698 | // gh-92138: For backward compatibility, functions of Python C API accepts 699 | // "const PyObject*". Check that using it does not emit C++ compiler 700 | // warnings. 701 | const PyObject *const_obj = obj; 702 | Py_INCREF(const_obj); 703 | Py_DECREF(const_obj); 704 | PyTypeObject *type = Py_TYPE(const_obj); 705 | assert(Py_REFCNT(const_obj) == refcnt); 706 | assert(type == &PyTuple_Type); 707 | assert(PyTuple_GET_SIZE(const_obj) == 2); 708 | PyObject *one = PyTuple_GET_ITEM(const_obj, 0); 709 | assert(PyLong_AsLong(one) == 1); 710 | 711 | #ifdef __cplusplus 712 | // gh-92898: StrongRef doesn't inherit from PyObject but has an operator to 713 | // cast to PyObject*. 714 | StrongRef strong_ref(obj); 715 | assert(Py_TYPE(strong_ref) == &PyTuple_Type); 716 | assert(Py_REFCNT(strong_ref) == (refcnt + 1)); 717 | Py_INCREF(strong_ref); 718 | Py_DECREF(strong_ref); 719 | #endif 720 | 721 | // gh-93442: Pass 0 as NULL for PyObject* 722 | Py_XINCREF(0); 723 | Py_XDECREF(0); 724 | #if _cplusplus >= 201103 725 | // Test nullptr passed as PyObject* 726 | Py_XINCREF(nullptr); 727 | Py_XDECREF(nullptr); 728 | #endif 729 | 730 | Py_DECREF(obj); 731 | Py_RETURN_NONE; 732 | } 733 | 734 | 735 | static PyObject * 736 | test_import(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 737 | { 738 | PyObject *mod = PyImport_ImportModule("sys"); 739 | if (mod == _Py_NULL) { 740 | return _Py_NULL; 741 | } 742 | Py_ssize_t refcnt = Py_REFCNT(mod); 743 | 744 | // test PyImport_AddModuleRef() 745 | PyObject *mod2 = PyImport_AddModuleRef("sys"); 746 | if (mod2 == _Py_NULL) { 747 | return _Py_NULL; 748 | } 749 | assert(PyModule_Check(mod2)); 750 | assert(Py_REFCNT(mod) == (refcnt + 1)); 751 | 752 | Py_DECREF(mod2); 753 | Py_DECREF(mod); 754 | 755 | Py_RETURN_NONE; 756 | } 757 | 758 | 759 | static void 760 | gc_collect(void) 761 | { 762 | #if defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000 763 | PyObject *mod = PyImport_ImportModule("gc"); 764 | assert(mod != _Py_NULL); 765 | 766 | PyObject *res = PyObject_CallMethod(mod, "collect", _Py_NULL); 767 | Py_DECREF(mod); 768 | assert(res != _Py_NULL); 769 | Py_DECREF(res); 770 | #else 771 | PyGC_Collect(); 772 | #endif 773 | } 774 | 775 | 776 | static PyObject * 777 | func_varargs(PyObject *Py_UNUSED(module), PyObject *args, PyObject *kwargs) 778 | { 779 | if (kwargs != _Py_NULL) { 780 | return PyTuple_Pack(2, args, kwargs); 781 | } 782 | else { 783 | return PyTuple_Pack(1, args); 784 | } 785 | } 786 | 787 | 788 | static void 789 | check_int(PyObject *obj, int value) 790 | { 791 | #ifdef PYTHON3 792 | assert(PyLong_Check(obj)); 793 | assert(PyLong_AsLong(obj) == value); 794 | #else 795 | assert(PyInt_Check(obj)); 796 | assert(PyInt_AsLong(obj) == value); 797 | #endif 798 | } 799 | 800 | 801 | static PyObject * 802 | test_weakref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 803 | { 804 | // Create a new heap type, create an instance of this type, and delete the 805 | // type. This object supports weak references. 806 | PyObject *new_type = PyObject_CallFunction((PyObject*)&PyType_Type, 807 | "s(){}", "TypeName"); 808 | if (new_type == _Py_NULL) { 809 | return _Py_NULL; 810 | } 811 | PyObject *obj = PyObject_CallNoArgs(new_type); 812 | Py_DECREF(new_type); 813 | if (obj == _Py_NULL) { 814 | return _Py_NULL; 815 | } 816 | Py_ssize_t refcnt = Py_REFCNT(obj); 817 | 818 | // create a weak reference 819 | PyObject *weakref = PyWeakref_NewRef(obj, _Py_NULL); 820 | if (weakref == _Py_NULL) { 821 | return _Py_NULL; 822 | } 823 | 824 | // test PyWeakref_GetRef(), reference is alive 825 | PyObject *ref = UNINITIALIZED_OBJ; 826 | assert(PyWeakref_GetRef(weakref, &ref) == 1); 827 | assert(ref == obj); 828 | assert(Py_REFCNT(obj) == (refcnt + 1)); 829 | Py_DECREF(ref); 830 | 831 | // delete the referenced object: clear the weakref 832 | Py_DECREF(obj); 833 | gc_collect(); 834 | 835 | // test PyWeakref_GetRef(), reference is dead 836 | ref = Py_True; 837 | assert(PyWeakref_GetRef(weakref, &ref) == 0); 838 | assert(ref == _Py_NULL); 839 | 840 | // test PyWeakref_GetRef(), invalid type 841 | PyObject *invalid_weakref = Py_None; 842 | assert(!PyErr_Occurred()); 843 | ref = Py_True; 844 | assert(PyWeakref_GetRef(invalid_weakref, &ref) == -1); 845 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 846 | assert(ref == _Py_NULL); 847 | PyErr_Clear(); 848 | 849 | #ifndef PYPY_VERSION 850 | // test PyWeakref_GetRef(NULL) 851 | ref = Py_True; 852 | assert(PyWeakref_GetRef(_Py_NULL, &ref) == -1); 853 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 854 | assert(ref == _Py_NULL); 855 | PyErr_Clear(); 856 | #endif 857 | 858 | Py_DECREF(weakref); 859 | Py_RETURN_NONE; 860 | } 861 | 862 | 863 | static void 864 | test_vectorcall_noargs(PyObject *func_varargs) 865 | { 866 | PyObject *res = PyObject_Vectorcall(func_varargs, _Py_NULL, 0, _Py_NULL); 867 | assert(res != _Py_NULL); 868 | 869 | assert(PyTuple_Check(res)); 870 | assert(PyTuple_GET_SIZE(res) == 1); 871 | PyObject *posargs = PyTuple_GET_ITEM(res, 0); 872 | 873 | assert(PyTuple_Check(posargs)); 874 | assert(PyTuple_GET_SIZE(posargs) == 0); 875 | 876 | Py_DECREF(res); 877 | } 878 | 879 | 880 | static void 881 | test_vectorcall_args(PyObject *func_varargs) 882 | { 883 | PyObject *args_tuple = Py_BuildValue("ii", 1, 2); 884 | assert(args_tuple != _Py_NULL); 885 | size_t nargs = (size_t)PyTuple_GET_SIZE(args_tuple); 886 | PyObject **args = &PyTuple_GET_ITEM(args_tuple, 0); 887 | 888 | PyObject *res = PyObject_Vectorcall(func_varargs, args, nargs, _Py_NULL); 889 | Py_DECREF(args_tuple); 890 | assert(res != _Py_NULL); 891 | 892 | assert(PyTuple_Check(res)); 893 | assert(PyTuple_GET_SIZE(res) == 1); 894 | PyObject *posargs = PyTuple_GET_ITEM(res, 0); 895 | 896 | assert(PyTuple_Check(posargs)); 897 | assert(PyTuple_GET_SIZE(posargs) == 2); 898 | check_int(PyTuple_GET_ITEM(posargs, 0), 1); 899 | check_int(PyTuple_GET_ITEM(posargs, 1), 2); 900 | 901 | Py_DECREF(res); 902 | } 903 | 904 | 905 | static void 906 | test_vectorcall_args_offset(PyObject *func_varargs) 907 | { 908 | // args contains 3 values, but only pass 2 last values 909 | PyObject *args_tuple = Py_BuildValue("iii", 1, 2, 3); 910 | assert(args_tuple != _Py_NULL); 911 | size_t nargs = 2 | PY_VECTORCALL_ARGUMENTS_OFFSET; 912 | PyObject **args = &PyTuple_GET_ITEM(args_tuple, 1); 913 | PyObject *arg0 = PyTuple_GET_ITEM(args_tuple, 0); 914 | 915 | PyObject *res = PyObject_Vectorcall(func_varargs, args, nargs, _Py_NULL); 916 | assert(PyTuple_GET_ITEM(args_tuple, 0) == arg0); 917 | Py_DECREF(args_tuple); 918 | assert(res != _Py_NULL); 919 | 920 | assert(PyTuple_Check(res)); 921 | assert(PyTuple_GET_SIZE(res) == 1); 922 | PyObject *posargs = PyTuple_GET_ITEM(res, 0); 923 | 924 | assert(PyTuple_Check(posargs)); 925 | assert(PyTuple_GET_SIZE(posargs) == 2); 926 | check_int(PyTuple_GET_ITEM(posargs, 0), 2); 927 | check_int(PyTuple_GET_ITEM(posargs, 1), 3); 928 | 929 | Py_DECREF(res); 930 | } 931 | 932 | 933 | static void 934 | test_vectorcall_args_kwnames(PyObject *func_varargs) 935 | { 936 | PyObject *args_tuple = Py_BuildValue("iiiii", 1, 2, 3, 4, 5); 937 | assert(args_tuple != _Py_NULL); 938 | PyObject **args = &PyTuple_GET_ITEM(args_tuple, 0); 939 | 940 | #ifdef PYTHON3 941 | PyObject *key1 = PyUnicode_FromString("key1"); 942 | PyObject *key2 = PyUnicode_FromString("key2"); 943 | #else 944 | PyObject *key1 = PyString_FromString("key1"); 945 | PyObject *key2 = PyString_FromString("key2"); 946 | #endif 947 | assert(key1 != _Py_NULL); 948 | assert(key2 != _Py_NULL); 949 | PyObject *kwnames = PyTuple_Pack(2, key1, key2); 950 | assert(kwnames != _Py_NULL); 951 | size_t nargs = (size_t)(PyTuple_GET_SIZE(args_tuple) - PyTuple_GET_SIZE(kwnames)); 952 | 953 | PyObject *res = PyObject_Vectorcall(func_varargs, args, nargs, kwnames); 954 | Py_DECREF(args_tuple); 955 | Py_DECREF(kwnames); 956 | assert(res != _Py_NULL); 957 | 958 | assert(PyTuple_Check(res)); 959 | assert(PyTuple_GET_SIZE(res) == 2); 960 | PyObject *posargs = PyTuple_GET_ITEM(res, 0); 961 | PyObject *kwargs = PyTuple_GET_ITEM(res, 1); 962 | 963 | assert(PyTuple_Check(posargs)); 964 | assert(PyTuple_GET_SIZE(posargs) == 3); 965 | check_int(PyTuple_GET_ITEM(posargs, 0), 1); 966 | check_int(PyTuple_GET_ITEM(posargs, 1), 2); 967 | check_int(PyTuple_GET_ITEM(posargs, 2), 3); 968 | 969 | assert(PyDict_Check(kwargs)); 970 | assert(PyDict_Size(kwargs) == 2); 971 | 972 | Py_ssize_t pos = 0; 973 | PyObject *key, *value; 974 | while (PyDict_Next(kwargs, &pos, &key, &value)) { 975 | #ifdef PYTHON3 976 | assert(PyUnicode_Check(key)); 977 | #else 978 | assert(PyString_Check(key)); 979 | #endif 980 | if (PyObject_RichCompareBool(key, key1, Py_EQ)) { 981 | check_int(value, 4); 982 | } 983 | else { 984 | assert(PyObject_RichCompareBool(key, key2, Py_EQ)); 985 | check_int(value, 5); 986 | } 987 | } 988 | 989 | Py_DECREF(res); 990 | Py_DECREF(key1); 991 | Py_DECREF(key2); 992 | } 993 | 994 | 995 | static PyObject * 996 | test_vectorcall(PyObject *module, PyObject *Py_UNUSED(args)) 997 | { 998 | #ifndef PYTHON3 999 | module = PyImport_ImportModule(MODULE_NAME_STR); 1000 | assert(module != _Py_NULL); 1001 | #endif 1002 | PyObject *func_varargs = PyObject_GetAttrString(module, "func_varargs"); 1003 | #ifndef PYTHON3 1004 | Py_DECREF(module); 1005 | #endif 1006 | if (func_varargs == _Py_NULL) { 1007 | return _Py_NULL; 1008 | } 1009 | 1010 | // test PyObject_Vectorcall() 1011 | test_vectorcall_noargs(func_varargs); 1012 | test_vectorcall_args(func_varargs); 1013 | test_vectorcall_args_offset(func_varargs); 1014 | test_vectorcall_args_kwnames(func_varargs); 1015 | 1016 | Py_DECREF(func_varargs); 1017 | Py_RETURN_NONE; 1018 | } 1019 | 1020 | 1021 | static PyObject * 1022 | test_getattr(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1023 | { 1024 | assert(!PyErr_Occurred()); 1025 | 1026 | PyObject *obj = PyImport_ImportModule("sys"); 1027 | if (obj == _Py_NULL) { 1028 | return _Py_NULL; 1029 | } 1030 | 1031 | PyObject *attr_name = create_string("version"); 1032 | PyObject *missing_attr = create_string("nonexistant_attr_name"); 1033 | 1034 | // test PyObject_GetOptionalAttr(): attribute exists 1035 | PyObject *value; 1036 | value = UNINITIALIZED_OBJ; 1037 | assert(PyObject_GetOptionalAttr(obj, attr_name, &value) == 1); 1038 | assert(value != _Py_NULL); 1039 | Py_DECREF(value); 1040 | 1041 | // test PyObject_HasAttrWithError(): attribute exists 1042 | assert(PyObject_HasAttrWithError(obj, attr_name) == 1); 1043 | 1044 | // test PyObject_GetOptionalAttrString(): attribute exists 1045 | value = UNINITIALIZED_OBJ; 1046 | assert(PyObject_GetOptionalAttrString(obj, "version", &value) == 1); 1047 | assert(!PyErr_Occurred()); 1048 | assert(value != _Py_NULL); 1049 | Py_DECREF(value); 1050 | 1051 | // test PyObject_HasAttrStringWithError(): attribute exists 1052 | assert(PyObject_HasAttrStringWithError(obj, "version") == 1); 1053 | assert(!PyErr_Occurred()); 1054 | 1055 | // test PyObject_GetOptionalAttr(): attribute doesn't exist 1056 | value = UNINITIALIZED_OBJ; 1057 | assert(PyObject_GetOptionalAttr(obj, missing_attr, &value) == 0); 1058 | assert(!PyErr_Occurred()); 1059 | assert(value == _Py_NULL); 1060 | 1061 | // test PyObject_HasAttrWithError(): attribute doesn't exist 1062 | assert(PyObject_HasAttrWithError(obj, missing_attr) == 0); 1063 | assert(!PyErr_Occurred()); 1064 | 1065 | // test PyObject_GetOptionalAttrString(): attribute doesn't exist 1066 | value = UNINITIALIZED_OBJ; 1067 | assert(PyObject_GetOptionalAttrString(obj, "nonexistant_attr_name", &value) == 0); 1068 | assert(!PyErr_Occurred()); 1069 | assert(value == _Py_NULL); 1070 | 1071 | // test PyObject_HasAttrStringWithError(): attribute doesn't exist 1072 | assert(PyObject_HasAttrStringWithError(obj, "nonexistant_attr_name") == 0); 1073 | assert(!PyErr_Occurred()); 1074 | 1075 | Py_DECREF(attr_name); 1076 | Py_DECREF(missing_attr); 1077 | Py_DECREF(obj); 1078 | Py_RETURN_NONE; 1079 | } 1080 | 1081 | 1082 | static PyObject * 1083 | test_getitem(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1084 | { 1085 | assert(!PyErr_Occurred()); 1086 | 1087 | PyObject *value = Py_BuildValue("s", "value"); 1088 | assert(value != _Py_NULL); 1089 | PyObject *obj = Py_BuildValue("{sO}", "key", value); 1090 | assert(obj != _Py_NULL); 1091 | PyObject *present_key, *missing_key; 1092 | PyObject *item; 1093 | 1094 | present_key = create_string("key"); 1095 | missing_key = create_string("dontexist"); 1096 | 1097 | // test PyMapping_GetOptionalItem(): key is present 1098 | item = UNINITIALIZED_OBJ; 1099 | assert(PyMapping_GetOptionalItem(obj, present_key, &item) == 1); 1100 | assert(item == value); 1101 | Py_DECREF(item); 1102 | assert(!PyErr_Occurred()); 1103 | 1104 | // test PyMapping_HasKeyWithError(): key is present 1105 | assert(PyMapping_HasKeyWithError(obj, present_key) == 1); 1106 | assert(!PyErr_Occurred()); 1107 | 1108 | // test PyMapping_GetOptionalItemString(): key is present 1109 | item = UNINITIALIZED_OBJ; 1110 | assert(PyMapping_GetOptionalItemString(obj, "key", &item) == 1); 1111 | assert(item == value); 1112 | Py_DECREF(item); 1113 | 1114 | // test PyMapping_HasKeyStringWithError(): key is present 1115 | assert(PyMapping_HasKeyStringWithError(obj, "key") == 1); 1116 | assert(!PyErr_Occurred()); 1117 | 1118 | // test PyMapping_GetOptionalItem(): missing key 1119 | item = UNINITIALIZED_OBJ; 1120 | assert(PyMapping_GetOptionalItem(obj, missing_key, &item) == 0); 1121 | assert(item == _Py_NULL); 1122 | assert(!PyErr_Occurred()); 1123 | 1124 | // test PyMapping_HasKeyWithError(): missing key 1125 | assert(PyMapping_HasKeyWithError(obj, missing_key) == 0); 1126 | assert(!PyErr_Occurred()); 1127 | 1128 | // test PyMapping_GetOptionalItemString(): missing key 1129 | item = UNINITIALIZED_OBJ; 1130 | assert(PyMapping_GetOptionalItemString(obj, "dontexist", &item) == 0); 1131 | assert(item == _Py_NULL); 1132 | 1133 | // test PyMapping_HasKeyStringWithError(): missing key 1134 | assert(PyMapping_HasKeyStringWithError(obj, "dontexist") == 0); 1135 | assert(!PyErr_Occurred()); 1136 | 1137 | Py_DECREF(obj); 1138 | Py_DECREF(value); 1139 | Py_DECREF(present_key); 1140 | Py_DECREF(missing_key); 1141 | Py_RETURN_NONE; 1142 | } 1143 | 1144 | 1145 | static PyObject * 1146 | test_dict_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1147 | { 1148 | assert(!PyErr_Occurred()); 1149 | 1150 | PyObject *dict = NULL, *key = NULL, *missing_key = NULL, *value = NULL; 1151 | PyObject *invalid_key = NULL; 1152 | PyObject *invalid_dict = NULL; 1153 | PyObject *get_value = NULL; 1154 | int res; 1155 | 1156 | // test PyDict_New() 1157 | dict = PyDict_New(); 1158 | if (dict == NULL) { 1159 | goto error; 1160 | } 1161 | 1162 | key = PyUnicode_FromString("key"); 1163 | if (key == NULL) { 1164 | goto error; 1165 | } 1166 | invalid_dict = key; // borrowed reference 1167 | 1168 | missing_key = PyUnicode_FromString("missing_key"); 1169 | if (missing_key == NULL) { 1170 | goto error; 1171 | } 1172 | 1173 | invalid_key = PyList_New(0); // not hashable key 1174 | if (invalid_key == NULL) { 1175 | goto error; 1176 | } 1177 | 1178 | value = PyUnicode_FromString("value"); 1179 | if (value == NULL) { 1180 | goto error; 1181 | } 1182 | 1183 | res = PyDict_SetItemString(dict, "key", value); 1184 | if (res < 0) { 1185 | goto error; 1186 | } 1187 | assert(res == 0); 1188 | 1189 | // test PyDict_Contains() 1190 | assert(PyDict_Contains(dict, key) == 1); 1191 | assert(PyDict_Contains(dict, missing_key) == 0); 1192 | 1193 | // test PyDict_ContainsString() 1194 | assert(PyDict_ContainsString(dict, "key") == 1); 1195 | assert(PyDict_ContainsString(dict, "missing_key") == 0); 1196 | assert(PyDict_ContainsString(dict, "\xff") == -1); 1197 | assert(PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)); 1198 | PyErr_Clear(); 1199 | 1200 | // test PyDict_GetItemRef(), key is present 1201 | get_value = UNINITIALIZED_OBJ; 1202 | assert(PyDict_GetItemRef(dict, key, &get_value) == 1); 1203 | assert(get_value == value); 1204 | Py_DECREF(get_value); 1205 | 1206 | // test PyDict_GetItemStringRef(), key is present 1207 | get_value = UNINITIALIZED_OBJ; 1208 | assert(PyDict_GetItemStringRef(dict, "key", &get_value) == 1); 1209 | assert(get_value == value); 1210 | Py_DECREF(get_value); 1211 | 1212 | // test PyDict_GetItemRef(), missing key 1213 | get_value = UNINITIALIZED_OBJ; 1214 | assert(PyDict_GetItemRef(dict, missing_key, &get_value) == 0); 1215 | assert(!PyErr_Occurred()); 1216 | assert(get_value == NULL); 1217 | 1218 | // test PyDict_GetItemStringRef(), missing key 1219 | get_value = UNINITIALIZED_OBJ; 1220 | assert(PyDict_GetItemStringRef(dict, "missing_key", &get_value) == 0); 1221 | assert(!PyErr_Occurred()); 1222 | assert(get_value == NULL); 1223 | 1224 | // test PyDict_GetItemRef(), invalid dict 1225 | get_value = UNINITIALIZED_OBJ; 1226 | assert(PyDict_GetItemRef(invalid_dict, key, &get_value) == -1); 1227 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 1228 | PyErr_Clear(); 1229 | assert(get_value == NULL); 1230 | 1231 | // test PyDict_GetItemStringRef(), invalid dict 1232 | get_value = UNINITIALIZED_OBJ; 1233 | assert(PyDict_GetItemStringRef(invalid_dict, "key", &get_value) == -1); 1234 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 1235 | PyErr_Clear(); 1236 | assert(get_value == NULL); 1237 | 1238 | // test PyDict_GetItemRef(), invalid key 1239 | get_value = UNINITIALIZED_OBJ; 1240 | assert(PyDict_GetItemRef(dict, invalid_key, &get_value) == -1); 1241 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 1242 | PyErr_Clear(); 1243 | assert(get_value == NULL); 1244 | 1245 | Py_DECREF(dict); 1246 | Py_DECREF(key); 1247 | Py_DECREF(missing_key); 1248 | Py_DECREF(value); 1249 | Py_DECREF(invalid_key); 1250 | 1251 | Py_RETURN_NONE; 1252 | 1253 | error: 1254 | Py_XDECREF(dict); 1255 | Py_XDECREF(key); 1256 | Py_XDECREF(missing_key); 1257 | Py_XDECREF(value); 1258 | Py_XDECREF(invalid_key); 1259 | return NULL; 1260 | } 1261 | 1262 | 1263 | static PyObject * 1264 | test_dict_pop(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1265 | { 1266 | PyObject *dict = PyDict_New(); 1267 | if (dict == NULL) { 1268 | return NULL; 1269 | } 1270 | 1271 | PyObject *key = PyUnicode_FromString("key"); 1272 | assert(key != NULL); 1273 | PyObject *value = PyUnicode_FromString("abc"); 1274 | assert(value != NULL); 1275 | 1276 | // test PyDict_Pop(), get the removed value, key is present 1277 | assert(PyDict_SetItem(dict, key, value) == 0); 1278 | PyObject *removed = UNINITIALIZED_OBJ; 1279 | assert(PyDict_Pop(dict, key, &removed) == 1); 1280 | assert(removed == value); 1281 | Py_DECREF(removed); 1282 | 1283 | // test PyDict_Pop(), ignore the removed value, key is present 1284 | assert(PyDict_SetItem(dict, key, value) == 0); 1285 | assert(PyDict_Pop(dict, key, NULL) == 1); 1286 | 1287 | // test PyDict_Pop(), key is missing 1288 | removed = UNINITIALIZED_OBJ; 1289 | assert(PyDict_Pop(dict, key, &removed) == 0); 1290 | assert(removed == NULL); 1291 | assert(PyDict_Pop(dict, key, NULL) == 0); 1292 | 1293 | // test PyDict_PopString(), get the removed value, key is present 1294 | assert(PyDict_SetItem(dict, key, value) == 0); 1295 | removed = UNINITIALIZED_OBJ; 1296 | assert(PyDict_PopString(dict, "key", &removed) == 1); 1297 | assert(removed == value); 1298 | Py_DECREF(removed); 1299 | 1300 | // test PyDict_PopString(), ignore the removed value, key is present 1301 | assert(PyDict_SetItem(dict, key, value) == 0); 1302 | assert(PyDict_PopString(dict, "key", NULL) == 1); 1303 | 1304 | // test PyDict_PopString(), key is missing 1305 | removed = UNINITIALIZED_OBJ; 1306 | assert(PyDict_PopString(dict, "key", &removed) == 0); 1307 | assert(removed == NULL); 1308 | assert(PyDict_PopString(dict, "key", NULL) == 0); 1309 | 1310 | // dict error 1311 | removed = UNINITIALIZED_OBJ; 1312 | assert(PyDict_Pop(key, key, &removed) == -1); 1313 | assert(removed == NULL); 1314 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 1315 | PyErr_Clear(); 1316 | 1317 | assert(PyDict_Pop(key, key, NULL) == -1); 1318 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 1319 | PyErr_Clear(); 1320 | 1321 | removed = UNINITIALIZED_OBJ; 1322 | assert(PyDict_PopString(key, "key", &removed) == -1); 1323 | assert(removed == NULL); 1324 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 1325 | PyErr_Clear(); 1326 | 1327 | assert(PyDict_PopString(key, "key", NULL) == -1); 1328 | assert(PyErr_ExceptionMatches(PyExc_SystemError)); 1329 | PyErr_Clear(); 1330 | 1331 | // exit 1332 | Py_DECREF(dict); 1333 | Py_DECREF(key); 1334 | Py_DECREF(value); 1335 | Py_RETURN_NONE; 1336 | } 1337 | 1338 | 1339 | static PyObject * 1340 | test_dict_setdefault(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1341 | { 1342 | PyObject *dict = PyDict_New(); 1343 | if (dict == NULL) { 1344 | return NULL; 1345 | } 1346 | PyObject *key = PyUnicode_FromString("key"); 1347 | assert(key != NULL); 1348 | PyObject *value = PyUnicode_FromString("abc"); 1349 | assert(value != NULL); 1350 | PyObject *invalid_key = PyList_New(0); // not hashable key 1351 | assert(invalid_key != NULL); 1352 | 1353 | // insert item 1354 | PyObject *result = UNINITIALIZED_OBJ; 1355 | assert(PyDict_SetDefaultRef(dict, key, value, &result) == 0); 1356 | assert(result == value); 1357 | Py_DECREF(result); 1358 | 1359 | // item already present 1360 | result = UNINITIALIZED_OBJ; 1361 | assert(PyDict_SetDefaultRef(dict, key, value, &result) == 1); 1362 | assert(result == value); 1363 | Py_DECREF(result); 1364 | 1365 | // error: invalid key 1366 | assert(!PyErr_Occurred()); 1367 | result = UNINITIALIZED_OBJ; 1368 | assert(PyDict_SetDefaultRef(dict, invalid_key, value, &result) == -1); 1369 | assert(result == NULL); 1370 | assert(PyErr_Occurred()); 1371 | PyErr_Clear(); 1372 | 1373 | // insert item with NULL result 1374 | assert(PyDict_Pop(dict, key, NULL) == 1); 1375 | assert(PyDict_SetDefaultRef(dict, key, value, NULL) == 0); 1376 | 1377 | // item already present with NULL result 1378 | assert(PyDict_SetDefaultRef(dict, key, value, NULL) == 1); 1379 | 1380 | // error: invalid key with NULL result 1381 | assert(!PyErr_Occurred()); 1382 | assert(PyDict_SetDefaultRef(dict, invalid_key, value, NULL) == -1); 1383 | assert(PyErr_Occurred()); 1384 | PyErr_Clear(); 1385 | 1386 | // exit 1387 | Py_DECREF(dict); 1388 | Py_DECREF(key); 1389 | Py_DECREF(value); 1390 | Py_DECREF(invalid_key); 1391 | Py_RETURN_NONE; 1392 | } 1393 | 1394 | 1395 | static PyObject * 1396 | test_long_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1397 | { 1398 | // test PyLong_AsInt() 1399 | assert(!PyErr_Occurred()); 1400 | PyObject *obj = PyLong_FromLong(123); 1401 | if (obj == NULL) { 1402 | return NULL; 1403 | } 1404 | int value = PyLong_AsInt(obj); 1405 | assert(value == 123); 1406 | assert(!PyErr_Occurred()); 1407 | Py_DECREF(obj); 1408 | 1409 | // test PyLong_AsInt() with overflow 1410 | PyObject *obj2 = PyLong_FromLongLong((long long)INT_MAX + 1); 1411 | if (obj2 == NULL) { 1412 | return NULL; 1413 | } 1414 | value = PyLong_AsInt(obj2); 1415 | assert(value == -1); 1416 | assert(PyErr_ExceptionMatches(PyExc_OverflowError)); 1417 | PyErr_Clear(); 1418 | Py_DECREF(obj2); 1419 | 1420 | // test PyLong_GetSign() 1421 | int sign = UNINITIALIZED_INT; 1422 | assert(PyLong_GetSign(obj, &sign) == 0); 1423 | assert(sign == 1); 1424 | 1425 | // test PyLong_IsPositive(), PyLong_IsNegative() and PyLong_IsZero() 1426 | assert(PyLong_IsPositive(obj) == 1); 1427 | assert(PyLong_IsNegative(obj) == 0); 1428 | assert(PyLong_IsZero(obj) == 0); 1429 | 1430 | #if defined(PYTHON3) && !defined(PYPY_VERSION) 1431 | // test import/export API 1432 | digit *digits; 1433 | PyLongWriter *writer; 1434 | static PyLongExport long_export; 1435 | 1436 | writer = PyLongWriter_Create(1, 1, (void **)&digits); 1437 | if (writer == NULL) { 1438 | return NULL; 1439 | } 1440 | PyLongWriter_Discard(writer); 1441 | 1442 | writer = PyLongWriter_Create(1, 1, (void **)&digits); 1443 | if (writer == NULL) { 1444 | return NULL; 1445 | } 1446 | digits[0] = 123; 1447 | obj = PyLongWriter_Finish(writer); 1448 | if (obj == NULL) { 1449 | return NULL; 1450 | } 1451 | 1452 | check_int(obj, -123); 1453 | if (PyLong_Export(obj, &long_export) < 0) { 1454 | return NULL; 1455 | } 1456 | assert(long_export.value == -123); 1457 | assert(long_export.digits == NULL); 1458 | PyLong_FreeExport(&long_export); 1459 | Py_DECREF(obj); 1460 | 1461 | writer = PyLongWriter_Create(0, 5, (void **)&digits); 1462 | if (writer == NULL) { 1463 | return NULL; 1464 | } 1465 | digits[0] = 1; 1466 | digits[1] = 0; 1467 | digits[2] = 0; 1468 | digits[3] = 0; 1469 | digits[4] = 1; 1470 | obj = PyLongWriter_Finish(writer); 1471 | if (obj == NULL) { 1472 | return NULL; 1473 | } 1474 | 1475 | if (PyLong_Export(obj, &long_export) < 0) { 1476 | return NULL; 1477 | } 1478 | assert(long_export.value == 0); 1479 | digits = (digit*)long_export.digits; 1480 | assert(digits[0] == 1); 1481 | assert(digits[1] == 0); 1482 | assert(digits[2] == 0); 1483 | assert(digits[3] == 0); 1484 | assert(digits[4] == 1); 1485 | PyLong_FreeExport(&long_export); 1486 | Py_DECREF(obj); 1487 | 1488 | const PyLongLayout *layout = PyLong_GetNativeLayout(); 1489 | assert(layout->digits_order == -1); 1490 | assert(layout->digit_size == sizeof(digit)); 1491 | #endif // defined(PYTHON3) && !defined(PYPY_VERSION) 1492 | 1493 | Py_RETURN_NONE; 1494 | } 1495 | 1496 | 1497 | // --- HeapCTypeWithManagedDict -------------------------------------------- 1498 | 1499 | // Py_TPFLAGS_MANAGED_DICT was added to Python 3.11.0a3 but is not implemented on PyPy 1500 | #if PY_VERSION_HEX >= 0x030B00A3 && ! defined(PYPY_VERSION) 1501 | # define TEST_MANAGED_DICT 1502 | 1503 | typedef struct { 1504 | PyObject_HEAD 1505 | } HeapCTypeObject; 1506 | 1507 | static int 1508 | heapmanaged_traverse(PyObject *self, visitproc visit, void *arg) 1509 | { 1510 | Py_VISIT(Py_TYPE(self)); 1511 | // Test PyObject_VisitManagedDict() 1512 | return PyObject_VisitManagedDict(self, visit, arg); 1513 | } 1514 | 1515 | static int 1516 | heapmanaged_clear(PyObject *self) 1517 | { 1518 | // Test PyObject_ClearManagedDict() 1519 | PyObject_ClearManagedDict(self); 1520 | return 0; 1521 | } 1522 | 1523 | static void 1524 | heapmanaged_dealloc(HeapCTypeObject *self) 1525 | { 1526 | PyTypeObject *tp = Py_TYPE(self); 1527 | PyObject_ClearManagedDict((PyObject *)self); 1528 | PyObject_GC_UnTrack(self); 1529 | PyObject_GC_Del(self); 1530 | Py_DECREF(tp); 1531 | } 1532 | 1533 | static PyType_Slot HeapCTypeWithManagedDict_slots[] = { 1534 | {Py_tp_traverse, _Py_CAST(void*, heapmanaged_traverse)}, 1535 | {Py_tp_clear, _Py_CAST(void*, heapmanaged_clear)}, 1536 | {Py_tp_dealloc, _Py_CAST(void*, heapmanaged_dealloc)}, 1537 | {0, 0}, 1538 | }; 1539 | 1540 | static PyType_Spec HeapCTypeWithManagedDict_spec = { 1541 | "test_pythoncapi_compat.HeapCTypeWithManagedDict", 1542 | sizeof(PyObject), 1543 | 0, 1544 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT, 1545 | HeapCTypeWithManagedDict_slots 1546 | }; 1547 | 1548 | static PyObject * 1549 | test_managed_dict(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1550 | { 1551 | // Test PyObject_VisitManagedDict() and PyObject_ClearManagedDict() 1552 | PyObject *type = PyType_FromSpec(&HeapCTypeWithManagedDict_spec); 1553 | if (type == NULL) { 1554 | return NULL; 1555 | } 1556 | 1557 | PyObject *obj = PyObject_CallNoArgs(type); 1558 | if (obj == NULL) { 1559 | Py_DECREF(type); 1560 | return NULL; 1561 | } 1562 | 1563 | // call heapmanaged_traverse() 1564 | PyGC_Collect(); 1565 | 1566 | // call heapmanaged_clear() 1567 | Py_DECREF(obj); 1568 | PyGC_Collect(); 1569 | 1570 | Py_DECREF(type); 1571 | // Just in case! 1572 | PyGC_Collect(); 1573 | 1574 | Py_RETURN_NONE; 1575 | } 1576 | #endif // PY_VERSION_HEX >= 0x030B00A3 1577 | 1578 | 1579 | static PyObject * 1580 | test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1581 | { 1582 | PyObject *abc = PyUnicode_FromString("abc"); 1583 | if (abc == NULL) { 1584 | return NULL; 1585 | } 1586 | 1587 | PyObject *abc0def = PyUnicode_FromStringAndSize("abc\0def", 7); 1588 | if (abc0def == NULL) { 1589 | Py_DECREF(abc); 1590 | return NULL; 1591 | } 1592 | 1593 | // PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() can be called 1594 | // with an exception raised and they must not clear the current exception. 1595 | PyErr_NoMemory(); 1596 | 1597 | assert(PyUnicode_EqualToUTF8AndSize(abc, "abc", 3) == 1); 1598 | assert(PyUnicode_EqualToUTF8AndSize(abc, "Python", 6) == 0); 1599 | assert(PyUnicode_EqualToUTF8AndSize(abc0def, "abc\0def", 7) == 1); 1600 | 1601 | assert(PyUnicode_EqualToUTF8(abc, "abc") == 1); 1602 | assert(PyUnicode_EqualToUTF8(abc, "Python") == 0); 1603 | assert(PyUnicode_EqualToUTF8(abc0def, "abc\0def") == 0); 1604 | 1605 | assert(PyErr_ExceptionMatches(PyExc_MemoryError)); 1606 | PyErr_Clear(); 1607 | 1608 | // Test PyUnicode_Equal() 1609 | assert(PyUnicode_Equal(abc, abc) == 1); 1610 | assert(PyUnicode_Equal(abc, abc0def) == 0); 1611 | assert(PyUnicode_Equal(abc, Py_True) == -1); 1612 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 1613 | PyErr_Clear(); 1614 | 1615 | // Test PyUnstable_Unicode_GET_CACHED_HASH() 1616 | #ifdef PYPY_VERSION 1617 | assert(PyUnstable_Unicode_GET_CACHED_HASH(abc) == -1); 1618 | #else 1619 | Py_hash_t hash = PyObject_Hash(abc); 1620 | assert(hash != -1); 1621 | assert(PyUnstable_Unicode_GET_CACHED_HASH(abc) == hash); 1622 | #endif 1623 | 1624 | Py_DECREF(abc); 1625 | Py_DECREF(abc0def); 1626 | Py_RETURN_NONE; 1627 | } 1628 | 1629 | 1630 | static PyObject * 1631 | test_list(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1632 | { 1633 | PyObject *list = PyList_New(0); 1634 | if (list == NULL) { 1635 | return NULL; 1636 | } 1637 | 1638 | // test PyList_Extend() 1639 | { 1640 | PyObject *abc = PyUnicode_FromString("abc"); 1641 | if (abc == NULL) { 1642 | Py_DECREF(list); 1643 | return NULL; 1644 | } 1645 | 1646 | assert(PyList_Extend(list, abc) == 0); 1647 | Py_DECREF(abc); 1648 | assert(PyList_GET_SIZE(list) == 3); 1649 | } 1650 | 1651 | // test PyList_GetItemRef() 1652 | PyObject *item = PyList_GetItemRef(list, 1); 1653 | assert(item != NULL); 1654 | assert(item == PyList_GetItem(list, 1)); 1655 | Py_DECREF(item); 1656 | 1657 | // test PyList_Clear() 1658 | assert(PyList_Clear(list) == 0); 1659 | assert(PyList_GET_SIZE(list) == 0); 1660 | 1661 | Py_DECREF(list); 1662 | Py_RETURN_NONE; 1663 | } 1664 | 1665 | 1666 | static PyObject * 1667 | test_hash(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1668 | { 1669 | void *ptr0 = NULL; 1670 | assert(Py_HashPointer(ptr0) == 0); 1671 | 1672 | #ifndef PYPY_VERSION 1673 | #if SIZEOF_VOID_P == 8 1674 | void *ptr1 = (void*)(uintptr_t)0xABCDEF1234567890; 1675 | assert(Py_HashPointer(ptr1) == (uintptr_t)0x0ABCDEF123456789); 1676 | #else 1677 | void *ptr1 = (void*)(uintptr_t)0xDEADCAFE; 1678 | assert(Py_HashPointer(ptr1) == (uintptr_t)0xEDEADCAF); 1679 | #endif 1680 | #else 1681 | // PyPy 1682 | #if SIZEOF_VOID_P == 8 1683 | void *ptr1 = (void*)(uintptr_t)0xABCDEF1234567890; 1684 | #else 1685 | void *ptr1 = (void*)(uintptr_t)0xDEADCAFE; 1686 | #endif 1687 | assert(Py_HashPointer(ptr1) == (Py_hash_t)ptr1); 1688 | #endif 1689 | 1690 | #if ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ 1691 | || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ 1692 | && PYPY_VERSION_NUM >= 0x07030800)) 1693 | // Just check that constants are available 1694 | size_t bits = PyHASH_BITS; 1695 | assert(bits >= 8); 1696 | size_t mod = PyHASH_MODULUS; 1697 | assert(mod >= 7); 1698 | size_t inf = PyHASH_INF; 1699 | assert(inf != 0); 1700 | size_t imag = PyHASH_IMAG; 1701 | assert(imag != 0); 1702 | #endif 1703 | 1704 | // Test Py_HashBuffer() 1705 | { 1706 | PyObject *abc = PyBytes_FromString("abc"); 1707 | if (abc == NULL) { 1708 | return NULL; 1709 | } 1710 | Py_hash_t hash = Py_HashBuffer(PyBytes_AS_STRING(abc), 1711 | PyBytes_GET_SIZE(abc)); 1712 | Py_hash_t hash2 = PyObject_Hash(abc); 1713 | assert(hash == hash2); 1714 | 1715 | Py_DECREF(abc); 1716 | } 1717 | 1718 | Py_RETURN_NONE; 1719 | } 1720 | 1721 | 1722 | #if PY_VERSION_HEX >= 0x03050000 1723 | #define TEST_PYTIME 1724 | 1725 | static PyObject * 1726 | test_time(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1727 | { 1728 | PyTime_t t; 1729 | #define UNINITIALIZED_TIME ((PyTime_t)-483884113929936179) 1730 | 1731 | t = UNINITIALIZED_TIME; 1732 | assert(PyTime_Time(&t) == 0); 1733 | assert(t != UNINITIALIZED_TIME); 1734 | 1735 | t = UNINITIALIZED_TIME; 1736 | assert(PyTime_Monotonic(&t) == 0); 1737 | assert(t != UNINITIALIZED_TIME); 1738 | 1739 | // Test multiple times since an implementation uses a cache 1740 | for (int i=0; i < 5; i++) { 1741 | t = UNINITIALIZED_TIME; 1742 | assert(PyTime_PerfCounter(&t) == 0); 1743 | assert(t != UNINITIALIZED_TIME); 1744 | } 1745 | 1746 | assert(PyTime_AsSecondsDouble(1) == 1e-9); 1747 | assert(PyTime_AsSecondsDouble(1500 * 1000 * 1000) == 1.5); 1748 | assert(PyTime_AsSecondsDouble(-500 * 1000 * 1000) == -0.5); 1749 | 1750 | Py_RETURN_NONE; 1751 | } 1752 | #endif 1753 | 1754 | 1755 | static void 1756 | check_get_constant(PyObject* (*get_constant)(unsigned int), int borrowed) 1757 | { 1758 | #define CLEAR(var) if (!borrowed) { Py_DECREF(var); } 1759 | 1760 | PyObject *obj, *expected; 1761 | 1762 | // Py_CONSTANT_NONE 1763 | obj = get_constant(Py_CONSTANT_NONE); 1764 | assert(obj == Py_None); 1765 | CLEAR(obj); 1766 | 1767 | // Py_CONSTANT_FALSE 1768 | obj = get_constant(Py_CONSTANT_FALSE); 1769 | assert(obj == Py_False); 1770 | CLEAR(obj); 1771 | 1772 | // Py_CONSTANT_TRUE 1773 | obj = get_constant(Py_CONSTANT_TRUE); 1774 | assert(obj == Py_True); 1775 | CLEAR(obj); 1776 | 1777 | // Py_CONSTANT_ELLIPSIS 1778 | obj = get_constant(Py_CONSTANT_ELLIPSIS); 1779 | assert(obj == Py_Ellipsis); 1780 | CLEAR(obj); 1781 | 1782 | // Py_CONSTANT_NOT_IMPLEMENTED 1783 | obj = get_constant(Py_CONSTANT_NOT_IMPLEMENTED); 1784 | assert(obj == Py_NotImplemented); 1785 | CLEAR(obj); 1786 | 1787 | // Py_CONSTANT_ZERO 1788 | obj = get_constant(Py_CONSTANT_ZERO); 1789 | expected = PyLong_FromLong(0); 1790 | assert(expected != NULL); 1791 | assert(Py_TYPE(obj) == &PyLong_Type); 1792 | assert(PyObject_RichCompareBool(obj, expected, Py_EQ) == 1); 1793 | CLEAR(obj); 1794 | Py_DECREF(expected); 1795 | 1796 | // Py_CONSTANT_ONE 1797 | obj = get_constant(Py_CONSTANT_ONE); 1798 | expected = PyLong_FromLong(1); 1799 | assert(expected != NULL); 1800 | assert(Py_TYPE(obj) == &PyLong_Type); 1801 | assert(PyObject_RichCompareBool(obj, expected, Py_EQ) == 1); 1802 | CLEAR(obj); 1803 | Py_DECREF(expected); 1804 | 1805 | // Py_CONSTANT_EMPTY_STR 1806 | obj = get_constant(Py_CONSTANT_EMPTY_STR); 1807 | assert(Py_TYPE(obj) == &PyUnicode_Type); 1808 | #if PY_VERSION_HEX >= 0x03030000 1809 | assert(PyUnicode_GetLength(obj) == 0); 1810 | #else 1811 | assert(PyUnicode_GetSize(obj) == 0); 1812 | #endif 1813 | CLEAR(obj); 1814 | 1815 | // Py_CONSTANT_EMPTY_BYTES 1816 | obj = get_constant(Py_CONSTANT_EMPTY_BYTES); 1817 | assert(Py_TYPE(obj) == &PyBytes_Type); 1818 | assert(PyBytes_Size(obj) == 0); 1819 | CLEAR(obj); 1820 | 1821 | // Py_CONSTANT_EMPTY_TUPLE 1822 | obj = get_constant(Py_CONSTANT_EMPTY_TUPLE); 1823 | assert(Py_TYPE(obj) == &PyTuple_Type); 1824 | assert(PyTuple_Size(obj) == 0); 1825 | CLEAR(obj); 1826 | 1827 | #undef CLEAR 1828 | } 1829 | 1830 | 1831 | static PyObject * 1832 | test_get_constant(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 1833 | { 1834 | check_get_constant(Py_GetConstant, 0); 1835 | check_get_constant(Py_GetConstantBorrowed, 1); 1836 | Py_RETURN_NONE; 1837 | } 1838 | 1839 | 1840 | #if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) 1841 | #define TEST_UNICODEWRITER 1 1842 | 1843 | static PyObject * 1844 | test_unicodewriter(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) 1845 | { 1846 | PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); 1847 | if (writer == NULL) { 1848 | return NULL; 1849 | } 1850 | int ret; 1851 | 1852 | // test PyUnicodeWriter_WriteStr() 1853 | PyObject *str = PyUnicode_FromString("var"); 1854 | if (str == NULL) { 1855 | goto error; 1856 | } 1857 | ret = PyUnicodeWriter_WriteStr(writer, str); 1858 | Py_CLEAR(str); 1859 | if (ret < 0) { 1860 | goto error; 1861 | } 1862 | 1863 | // test PyUnicodeWriter_WriteChar() 1864 | if (PyUnicodeWriter_WriteChar(writer, '=') < 0) { 1865 | goto error; 1866 | } 1867 | 1868 | // test PyUnicodeWriter_WriteSubstring() 1869 | str = PyUnicode_FromString("[long]"); 1870 | if (str == NULL) { 1871 | goto error; 1872 | } 1873 | ret = PyUnicodeWriter_WriteSubstring(writer, str, 1, 5); 1874 | Py_CLEAR(str); 1875 | if (ret < 0) { 1876 | goto error; 1877 | } 1878 | 1879 | // test PyUnicodeWriter_WriteASCII() 1880 | if (PyUnicodeWriter_WriteASCII(writer, " non-ASCII", -1) < 0) { 1881 | goto error; 1882 | } 1883 | 1884 | // test PyUnicodeWriter_WriteUTF8() 1885 | if (PyUnicodeWriter_WriteUTF8(writer, " valu\xC3\xA9", -1) < 0) { 1886 | goto error; 1887 | } 1888 | if (PyUnicodeWriter_WriteChar(writer, ' ') < 0) { 1889 | goto error; 1890 | } 1891 | 1892 | // test PyUnicodeWriter_WriteRepr() 1893 | str = PyUnicode_FromString("repr"); 1894 | if (str == NULL) { 1895 | goto error; 1896 | } 1897 | if (PyUnicodeWriter_WriteRepr(writer, str) < 0) { 1898 | goto error; 1899 | } 1900 | Py_CLEAR(str); 1901 | 1902 | { 1903 | PyObject *result = PyUnicodeWriter_Finish(writer); 1904 | if (result == NULL) { 1905 | return NULL; 1906 | } 1907 | assert(PyUnicode_EqualToUTF8(result, "var=long non-ASCII valu\xC3\xA9 'repr'")); 1908 | Py_DECREF(result); 1909 | } 1910 | 1911 | Py_RETURN_NONE; 1912 | 1913 | error: 1914 | PyUnicodeWriter_Discard(writer); 1915 | return NULL; 1916 | } 1917 | 1918 | 1919 | static PyObject * 1920 | test_unicodewriter_widechar(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) 1921 | { 1922 | PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); 1923 | if (writer == NULL) { 1924 | return NULL; 1925 | } 1926 | 1927 | // test PyUnicodeWriter_WriteWideChar() 1928 | int ret = PyUnicodeWriter_WriteWideChar(writer, L"euro=\u20AC", -1); 1929 | if (ret < 0) { 1930 | goto error; 1931 | } 1932 | 1933 | { 1934 | PyObject *result = PyUnicodeWriter_Finish(writer); 1935 | if (result == NULL) { 1936 | return NULL; 1937 | } 1938 | assert(PyUnicode_EqualToUTF8(result, "euro=\xe2\x82\xac")); 1939 | Py_DECREF(result); 1940 | } 1941 | 1942 | Py_RETURN_NONE; 1943 | 1944 | error: 1945 | PyUnicodeWriter_Discard(writer); 1946 | return NULL; 1947 | } 1948 | 1949 | 1950 | static PyObject * 1951 | test_unicodewriter_format(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) 1952 | { 1953 | PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); 1954 | if (writer == NULL) { 1955 | return NULL; 1956 | } 1957 | 1958 | // test PyUnicodeWriter_Format() 1959 | if (PyUnicodeWriter_Format(writer, "%s %i", "Hello", 123) < 0) { 1960 | goto error; 1961 | } 1962 | 1963 | // test PyUnicodeWriter_WriteChar() 1964 | if (PyUnicodeWriter_WriteChar(writer, '.') < 0) { 1965 | goto error; 1966 | } 1967 | 1968 | { 1969 | PyObject *result = PyUnicodeWriter_Finish(writer); 1970 | if (result == NULL) { 1971 | return NULL; 1972 | } 1973 | assert(PyUnicode_EqualToUTF8(result, "Hello 123.")); 1974 | Py_DECREF(result); 1975 | } 1976 | 1977 | Py_RETURN_NONE; 1978 | 1979 | error: 1980 | PyUnicodeWriter_Discard(writer); 1981 | return NULL; 1982 | } 1983 | #endif 1984 | 1985 | static PyObject * 1986 | test_uniquely_referenced(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) 1987 | { 1988 | PyObject *obj = Py_BuildValue("(s, s)", "hello", "world"); 1989 | if (obj == NULL) { 1990 | return NULL; 1991 | } 1992 | 1993 | assert(PyUnstable_Object_IsUniquelyReferenced(obj)); 1994 | 1995 | Py_INCREF(obj); 1996 | 1997 | assert(!PyUnstable_Object_IsUniquelyReferenced(obj)); 1998 | 1999 | Py_DECREF(obj); 2000 | Py_DECREF(obj); 2001 | 2002 | Py_RETURN_NONE; 2003 | } 2004 | 2005 | static PyObject * 2006 | test_bytes(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2007 | { 2008 | // Test PyBytes_Join() 2009 | PyObject *abc = PyBytes_FromString("a b c"); 2010 | if (abc == NULL) { 2011 | return NULL; 2012 | } 2013 | PyObject *list = PyObject_CallMethod(abc, "split", NULL); 2014 | Py_DECREF(abc); 2015 | if (list == NULL) { 2016 | return NULL; 2017 | } 2018 | PyObject *sep = PyBytes_FromString("-"); 2019 | if (sep == NULL) { 2020 | Py_DECREF(list); 2021 | return NULL; 2022 | } 2023 | 2024 | PyObject *join = PyBytes_Join(sep, list); 2025 | assert(join != NULL); 2026 | assert(PyBytes_Check(join)); 2027 | assert(memcmp(PyBytes_AS_STRING(join), "a-b-c", 5) == 0); 2028 | Py_DECREF(join); 2029 | 2030 | Py_DECREF(list); 2031 | Py_DECREF(sep); 2032 | Py_RETURN_NONE; 2033 | } 2034 | 2035 | 2036 | static PyObject * 2037 | test_iter(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2038 | { 2039 | // Test PyIter_NextItem() 2040 | PyObject *tuple = Py_BuildValue("(i)", 123); 2041 | if (tuple == NULL) { 2042 | return NULL; 2043 | } 2044 | PyObject *iter = PyObject_GetIter(tuple); 2045 | Py_DECREF(tuple); 2046 | if (iter == NULL) { 2047 | return NULL; 2048 | } 2049 | 2050 | // first item 2051 | PyObject *item = UNINITIALIZED_OBJ; 2052 | assert(PyIter_NextItem(iter, &item) == 1); 2053 | { 2054 | PyObject *expected = PyLong_FromLong(123); 2055 | assert(PyObject_RichCompareBool(item, expected, Py_EQ) == 1); 2056 | assert(expected != NULL); 2057 | Py_DECREF(expected); 2058 | } 2059 | 2060 | // StopIteration 2061 | item = UNINITIALIZED_OBJ; 2062 | assert(PyIter_NextItem(iter, &item) == 0); 2063 | assert(item == NULL); 2064 | assert(!PyErr_Occurred()); 2065 | 2066 | // non-iterable object 2067 | item = UNINITIALIZED_OBJ; 2068 | assert(PyIter_NextItem(Py_None, &item) == -1); 2069 | assert(item == NULL); 2070 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 2071 | PyErr_Clear(); 2072 | 2073 | Py_DECREF(iter); 2074 | Py_RETURN_NONE; 2075 | } 2076 | 2077 | 2078 | static PyObject * 2079 | test_long_stdint(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2080 | { 2081 | PyObject *obj; 2082 | 2083 | // Test PyLong_FromInt32() and PyLong_AsInt32() 2084 | obj = PyLong_FromInt32(INT32_C(-0x12345678)); 2085 | assert(obj != NULL); 2086 | int32_t i32; 2087 | assert(PyLong_AsInt32(obj, &i32) == 0); 2088 | assert(i32 == INT32_C(-0x12345678)); 2089 | Py_DECREF(obj); 2090 | 2091 | // Test PyLong_FromUInt32() and PyLong_AsUInt32() 2092 | obj = PyLong_FromUInt32(UINT32_C(0xDEADBEEF)); 2093 | assert(obj != NULL); 2094 | uint32_t u32; 2095 | assert(PyLong_AsUInt32(obj, &u32) == 0); 2096 | assert(u32 == UINT32_C(0xDEADBEEF)); 2097 | Py_DECREF(obj); 2098 | 2099 | // Test PyLong_FromInt64() and PyLong_AsInt64() 2100 | obj = PyLong_FromInt64(INT64_C(-0x12345678DEADBEEF)); 2101 | assert(obj != NULL); 2102 | int64_t i64; 2103 | assert(PyLong_AsInt64(obj, &i64) == 0); 2104 | assert(i64 == INT64_C(-0x12345678DEADBEEF)); 2105 | Py_DECREF(obj); 2106 | 2107 | // Test PyLong_FromUInt64() and PyLong_AsUInt64() 2108 | obj = PyLong_FromUInt64(UINT64_C(0xDEADBEEF12345678)); 2109 | assert(obj != NULL); 2110 | uint64_t u64; 2111 | assert(PyLong_AsUInt64(obj, &u64) == 0); 2112 | assert(u64 == UINT64_C(0xDEADBEEF12345678)); 2113 | Py_DECREF(obj); 2114 | 2115 | Py_RETURN_NONE; 2116 | } 2117 | 2118 | 2119 | static PyObject * 2120 | test_structmember(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2121 | { 2122 | assert(Py_T_SHORT == T_SHORT); 2123 | assert(Py_T_SHORT == T_SHORT); 2124 | assert(Py_T_INT == T_INT); 2125 | assert(Py_T_LONG == T_LONG); 2126 | assert(Py_T_FLOAT == T_FLOAT); 2127 | assert(Py_T_DOUBLE == T_DOUBLE); 2128 | assert(Py_T_STRING == T_STRING); 2129 | assert(_Py_T_OBJECT == T_OBJECT); 2130 | assert(Py_T_CHAR == T_CHAR); 2131 | assert(Py_T_BYTE == T_BYTE); 2132 | assert(Py_T_UBYTE == T_UBYTE); 2133 | assert(Py_T_USHORT == T_USHORT); 2134 | assert(Py_T_UINT == T_UINT); 2135 | assert(Py_T_ULONG == T_ULONG); 2136 | assert(Py_T_STRING_INPLACE == T_STRING_INPLACE); 2137 | assert(Py_T_BOOL == T_BOOL); 2138 | assert(Py_T_OBJECT_EX == T_OBJECT_EX); 2139 | assert(Py_T_LONGLONG == T_LONGLONG); 2140 | assert(Py_T_ULONGLONG == T_ULONGLONG); 2141 | assert(Py_T_PYSSIZET == T_PYSSIZET); 2142 | #if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) 2143 | assert(_Py_T_NONE == T_NONE); 2144 | #endif 2145 | assert(Py_READONLY == READONLY); 2146 | assert(Py_AUDIT_READ == READ_RESTRICTED); 2147 | assert(_Py_WRITE_RESTRICTED == PY_WRITE_RESTRICTED); 2148 | 2149 | Py_RETURN_NONE; 2150 | } 2151 | 2152 | 2153 | static PyObject * 2154 | test_file(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2155 | { 2156 | const char *filename = __FILE__; 2157 | PyObject *path = create_string(filename); 2158 | 2159 | FILE *fp = Py_fopen(path, "rb"); 2160 | Py_DECREF(path); 2161 | assert(fp != NULL); 2162 | Py_fclose(fp); 2163 | 2164 | Py_RETURN_NONE; 2165 | } 2166 | 2167 | 2168 | #if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION) 2169 | static PyObject * 2170 | test_config(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2171 | { 2172 | // Test PyConfig_Get() 2173 | PyObject *sys = PyImport_ImportModule("sys"); 2174 | if (sys == _Py_NULL) { 2175 | return _Py_NULL; 2176 | } 2177 | 2178 | PyObject *obj = PyConfig_Get("argv"); 2179 | PyObject *sys_attr = PyObject_GetAttrString(sys, "argv"); 2180 | assert(obj == sys_attr); 2181 | Py_DECREF(obj); 2182 | Py_DECREF(sys_attr); 2183 | 2184 | obj = PyConfig_Get("module_search_paths"); 2185 | sys_attr = PyObject_GetAttrString(sys, "path"); 2186 | assert(obj == sys_attr); 2187 | Py_DECREF(obj); 2188 | Py_DECREF(sys_attr); 2189 | 2190 | obj = PyConfig_Get("xoptions"); 2191 | sys_attr = PyObject_GetAttrString(sys, "_xoptions"); 2192 | assert(obj == sys_attr); 2193 | Py_DECREF(obj); 2194 | Py_DECREF(sys_attr); 2195 | 2196 | obj = PyConfig_Get("use_environment"); 2197 | assert(PyBool_Check(obj)); 2198 | Py_DECREF(obj); 2199 | 2200 | obj = PyConfig_Get("verbose"); 2201 | assert(PyLong_Check(obj)); 2202 | Py_DECREF(obj); 2203 | 2204 | // Get the last member 2205 | #if 0x030A0000 <= PY_VERSION_HEX 2206 | obj = PyConfig_Get("warn_default_encoding"); 2207 | #else 2208 | obj = PyConfig_Get("user_site_directory"); 2209 | #endif 2210 | assert(PyLong_Check(obj)); 2211 | Py_DECREF(obj); 2212 | 2213 | assert(PyConfig_Get("nonexistent") == NULL); 2214 | assert(PyErr_ExceptionMatches(PyExc_ValueError)); 2215 | PyErr_Clear(); 2216 | 2217 | // Test PyConfig_GetInt() 2218 | int value = -3; 2219 | 2220 | assert(PyConfig_GetInt("verbose", &value) == 0); 2221 | assert(value >= 0); 2222 | 2223 | assert(PyConfig_GetInt("argv", &value) == -1); 2224 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 2225 | PyErr_Clear(); 2226 | 2227 | assert(PyConfig_GetInt("nonexistent", &value) == -1); 2228 | assert(PyErr_ExceptionMatches(PyExc_ValueError)); 2229 | PyErr_Clear(); 2230 | 2231 | Py_DECREF(sys); 2232 | Py_RETURN_NONE; 2233 | } 2234 | #endif 2235 | 2236 | 2237 | static PyObject * 2238 | test_sys(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2239 | { 2240 | const char *stdout_str = "stdout"; 2241 | PyObject *stdout_obj = create_string(stdout_str); 2242 | #if PYTHON3 2243 | PyObject *sys_stdout = PySys_GetObject(stdout_str); // borrowed ref 2244 | #else 2245 | PyObject *sys_stdout = PySys_GetObject((char*)stdout_str); // borrowed ref 2246 | #endif 2247 | const char *nonexistent_str = "nonexistent"; 2248 | PyObject *nonexistent_obj = create_string(nonexistent_str); 2249 | PyObject *error_obj = PyLong_FromLong(1); 2250 | PyObject *value; 2251 | 2252 | // get sys.stdout 2253 | value = PySys_GetAttr(stdout_obj); 2254 | assert(value == sys_stdout); 2255 | Py_DECREF(value); 2256 | 2257 | value = PySys_GetAttrString(stdout_str); 2258 | assert(value == sys_stdout); 2259 | Py_DECREF(value); 2260 | 2261 | value = UNINITIALIZED_OBJ; 2262 | assert(PySys_GetOptionalAttr(stdout_obj, &value) == 1); 2263 | assert(value == sys_stdout); 2264 | Py_DECREF(value); 2265 | 2266 | value = UNINITIALIZED_OBJ; 2267 | assert(PySys_GetOptionalAttrString(stdout_str, &value) == 1); 2268 | assert(value == sys_stdout); 2269 | Py_DECREF(value); 2270 | 2271 | // non existent attribute 2272 | value = PySys_GetAttr(nonexistent_obj); 2273 | assert(value == NULL); 2274 | assert(PyErr_ExceptionMatches(PyExc_RuntimeError)); 2275 | PyErr_Clear(); 2276 | 2277 | value = PySys_GetAttrString(nonexistent_str); 2278 | assert(value == NULL); 2279 | assert(PyErr_ExceptionMatches(PyExc_RuntimeError)); 2280 | PyErr_Clear(); 2281 | 2282 | value = UNINITIALIZED_OBJ; 2283 | assert(PySys_GetOptionalAttr(nonexistent_obj, &value) == 0); 2284 | assert(value == NULL); 2285 | 2286 | value = UNINITIALIZED_OBJ; 2287 | assert(PySys_GetOptionalAttrString(nonexistent_str, &value) == 0); 2288 | assert(value == NULL); 2289 | 2290 | // invalid attribute type 2291 | value = PySys_GetAttr(error_obj); 2292 | assert(value == NULL); 2293 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 2294 | PyErr_Clear(); 2295 | 2296 | value = UNINITIALIZED_OBJ; 2297 | assert(PySys_GetOptionalAttr(error_obj, &value) == -1); 2298 | assert(value == NULL); 2299 | assert(PyErr_ExceptionMatches(PyExc_TypeError)); 2300 | PyErr_Clear(); 2301 | 2302 | Py_DECREF(stdout_obj); 2303 | Py_DECREF(nonexistent_obj); 2304 | Py_RETURN_NONE; 2305 | } 2306 | 2307 | 2308 | static int 2309 | test_byteswriter_highlevel(void) 2310 | { 2311 | PyObject *obj; 2312 | PyBytesWriter *writer = PyBytesWriter_Create(0); 2313 | if (writer == NULL) { 2314 | goto error; 2315 | } 2316 | if (PyBytesWriter_WriteBytes(writer, "Hello", -1) < 0) { 2317 | goto error; 2318 | } 2319 | if (PyBytesWriter_Format(writer, " %s!", "World") < 0) { 2320 | goto error; 2321 | } 2322 | 2323 | obj = PyBytesWriter_Finish(writer); 2324 | if (obj == NULL) { 2325 | return -1; 2326 | } 2327 | assert(PyBytes_Check(obj)); 2328 | assert(strcmp(PyBytes_AS_STRING(obj), "Hello World!") == 0); 2329 | Py_DECREF(obj); 2330 | return 0; 2331 | 2332 | error: 2333 | PyBytesWriter_Discard(writer); 2334 | return -1; 2335 | } 2336 | 2337 | static int 2338 | test_byteswriter_abc(void) 2339 | { 2340 | PyBytesWriter *writer = PyBytesWriter_Create(3); 2341 | if (writer == NULL) { 2342 | return -1; 2343 | } 2344 | 2345 | char *str = (char*)PyBytesWriter_GetData(writer); 2346 | memcpy(str, "abc", 3); 2347 | 2348 | PyObject *obj = PyBytesWriter_Finish(writer); 2349 | if (obj == NULL) { 2350 | return -1; 2351 | } 2352 | assert(PyBytes_Check(obj)); 2353 | assert(strcmp(PyBytes_AS_STRING(obj), "abc") == 0); 2354 | Py_DECREF(obj); 2355 | return 0; 2356 | } 2357 | 2358 | static int 2359 | test_byteswriter_grow(void) 2360 | { 2361 | PyBytesWriter *writer = PyBytesWriter_Create(10); 2362 | if (writer == NULL) { 2363 | return -1; 2364 | } 2365 | 2366 | char *buf = (char*)PyBytesWriter_GetData(writer); 2367 | memcpy(buf, "Hello ", strlen("Hello ")); 2368 | buf += strlen("Hello "); 2369 | 2370 | buf = (char*)PyBytesWriter_GrowAndUpdatePointer(writer, 10, buf); 2371 | if (buf == NULL) { 2372 | PyBytesWriter_Discard(writer); 2373 | return -1; 2374 | } 2375 | 2376 | memcpy(buf, "World", strlen("World")); 2377 | buf += strlen("World"); 2378 | 2379 | PyObject *obj = PyBytesWriter_FinishWithPointer(writer, buf); 2380 | if (obj == NULL) { 2381 | return -1; 2382 | } 2383 | assert(PyBytes_Check(obj)); 2384 | assert(strcmp(PyBytes_AS_STRING(obj), "Hello World") == 0); 2385 | Py_DECREF(obj); 2386 | return 0; 2387 | } 2388 | 2389 | static PyObject * 2390 | test_byteswriter(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2391 | { 2392 | if (test_byteswriter_highlevel() < 0) { 2393 | return NULL; 2394 | } 2395 | if (test_byteswriter_abc() < 0) { 2396 | return NULL; 2397 | } 2398 | if (test_byteswriter_grow() < 0) { 2399 | return NULL; 2400 | } 2401 | Py_RETURN_NONE; 2402 | } 2403 | 2404 | 2405 | static PyObject* 2406 | test_tuple_fromarray(void) 2407 | { 2408 | PyObject* array[] = { 2409 | PyLong_FromLong(1), 2410 | PyLong_FromLong(2), 2411 | PyLong_FromLong(3) 2412 | }; 2413 | PyObject *tuple = PyTuple_FromArray(array, 3); 2414 | if (tuple == NULL) { 2415 | goto error; 2416 | } 2417 | 2418 | assert(PyTuple_GET_SIZE(tuple) == 3); 2419 | assert(PyTuple_GET_ITEM(tuple, 0) == array[0]); 2420 | assert(PyTuple_GET_ITEM(tuple, 1) == array[1]); 2421 | assert(PyTuple_GET_ITEM(tuple, 2) == array[2]); 2422 | 2423 | Py_DECREF(tuple); 2424 | Py_DECREF(array[0]); 2425 | Py_DECREF(array[1]); 2426 | Py_DECREF(array[2]); 2427 | 2428 | // Test PyTuple_FromArray(NULL, 0) 2429 | tuple = PyTuple_FromArray(NULL, 0); 2430 | if (tuple == NULL) { 2431 | return NULL; 2432 | } 2433 | assert(PyTuple_GET_SIZE(tuple) == 0); 2434 | Py_DECREF(tuple); 2435 | 2436 | Py_RETURN_NONE; 2437 | 2438 | error: 2439 | Py_DECREF(array[0]); 2440 | Py_DECREF(array[1]); 2441 | Py_DECREF(array[2]); 2442 | return NULL; 2443 | } 2444 | 2445 | 2446 | static PyObject* 2447 | test_tuple(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2448 | { 2449 | return test_tuple_fromarray(); 2450 | } 2451 | 2452 | // Test adapted from CPython's _testcapi/object.c 2453 | static int TryIncref_dealloc_called = 0; 2454 | 2455 | static void 2456 | TryIncref_dealloc(PyObject *op) 2457 | { 2458 | // PyUnstable_TryIncRef should return 0 if object is being deallocated 2459 | assert(Py_REFCNT(op) == 0); 2460 | assert(!PyUnstable_TryIncRef(op)); 2461 | assert(Py_REFCNT(op) == 0); 2462 | 2463 | TryIncref_dealloc_called++; 2464 | Py_TYPE(op)->tp_free(op); 2465 | } 2466 | 2467 | static PyTypeObject TryIncrefType; 2468 | 2469 | static PyObject* 2470 | test_try_incref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) 2471 | { 2472 | TryIncref_dealloc_called = 0; 2473 | 2474 | PyObject *obj = PyObject_New(PyObject, &TryIncrefType); 2475 | if (obj == _Py_NULL) { 2476 | return _Py_NULL; 2477 | } 2478 | 2479 | PyUnstable_EnableTryIncRef(obj); 2480 | 2481 | Py_ssize_t refcount = Py_REFCNT(obj); 2482 | assert(PyUnstable_TryIncRef(obj)); 2483 | assert(Py_REFCNT(obj) == refcount + 1); 2484 | 2485 | Py_DECREF(obj); 2486 | Py_DECREF(obj); 2487 | 2488 | assert(TryIncref_dealloc_called == 1); 2489 | Py_RETURN_NONE; 2490 | } 2491 | 2492 | 2493 | static struct PyMethodDef methods[] = { 2494 | {"test_object", test_object, METH_NOARGS, _Py_NULL}, 2495 | {"test_py_is", test_py_is, METH_NOARGS, _Py_NULL}, 2496 | #ifndef PYPY_VERSION 2497 | {"test_frame", test_frame, METH_NOARGS, _Py_NULL}, 2498 | #endif 2499 | {"test_thread_state", test_thread_state, METH_NOARGS, _Py_NULL}, 2500 | {"test_interpreter", test_interpreter, METH_NOARGS, _Py_NULL}, 2501 | {"test_calls", test_calls, METH_NOARGS, _Py_NULL}, 2502 | {"test_gc", test_gc, METH_NOARGS, _Py_NULL}, 2503 | {"test_module", test_module, METH_NOARGS, _Py_NULL}, 2504 | #if (PY_VERSION_HEX <= 0x030B00A1 || 0x030B00A7 <= PY_VERSION_HEX) && !defined(PYPY_VERSION) 2505 | {"test_float_pack", test_float_pack, METH_NOARGS, _Py_NULL}, 2506 | #endif 2507 | #ifndef PYPY_VERSION 2508 | {"test_code", test_code, METH_NOARGS, _Py_NULL}, 2509 | #endif 2510 | {"test_api_casts", test_api_casts, METH_NOARGS, _Py_NULL}, 2511 | {"test_import", test_import, METH_NOARGS, _Py_NULL}, 2512 | {"test_weakref", test_weakref, METH_NOARGS, _Py_NULL}, 2513 | {"func_varargs", (PyCFunction)(void*)func_varargs, METH_VARARGS | METH_KEYWORDS, _Py_NULL}, 2514 | {"test_vectorcall", test_vectorcall, METH_NOARGS, _Py_NULL}, 2515 | {"test_getattr", test_getattr, METH_NOARGS, _Py_NULL}, 2516 | {"test_getitem", test_getitem, METH_NOARGS, _Py_NULL}, 2517 | {"test_dict_api", test_dict_api, METH_NOARGS, _Py_NULL}, 2518 | {"test_dict_pop", test_dict_pop, METH_NOARGS, _Py_NULL}, 2519 | {"test_dict_setdefault", test_dict_setdefault, METH_NOARGS, _Py_NULL}, 2520 | {"test_long_api", test_long_api, METH_NOARGS, _Py_NULL}, 2521 | #ifdef TEST_MANAGED_DICT 2522 | {"test_managed_dict", test_managed_dict, METH_NOARGS, _Py_NULL}, 2523 | #endif 2524 | {"test_unicode", test_unicode, METH_NOARGS, _Py_NULL}, 2525 | {"test_list", test_list, METH_NOARGS, _Py_NULL}, 2526 | {"test_hash", test_hash, METH_NOARGS, _Py_NULL}, 2527 | #ifdef TEST_PYTIME 2528 | {"test_time", test_time, METH_NOARGS, _Py_NULL}, 2529 | #endif 2530 | {"test_get_constant", test_get_constant, METH_NOARGS, _Py_NULL}, 2531 | #ifdef TEST_UNICODEWRITER 2532 | {"test_unicodewriter", test_unicodewriter, METH_NOARGS, _Py_NULL}, 2533 | {"test_unicodewriter_widechar", test_unicodewriter_widechar, METH_NOARGS, _Py_NULL}, 2534 | {"test_unicodewriter_format", test_unicodewriter_format, METH_NOARGS, _Py_NULL}, 2535 | #endif 2536 | {"test_bytes", test_bytes, METH_NOARGS, _Py_NULL}, 2537 | {"test_iter", test_iter, METH_NOARGS, _Py_NULL}, 2538 | {"test_long_stdint", test_long_stdint, METH_NOARGS, _Py_NULL}, 2539 | {"test_structmember", test_structmember, METH_NOARGS, _Py_NULL}, 2540 | {"test_file", test_file, METH_NOARGS, _Py_NULL}, 2541 | #if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION) 2542 | {"test_config", test_config, METH_NOARGS, _Py_NULL}, 2543 | #endif 2544 | {"test_sys", test_sys, METH_NOARGS, _Py_NULL}, 2545 | {"test_uniquely_referenced", test_uniquely_referenced, METH_NOARGS, _Py_NULL}, 2546 | {"test_byteswriter", test_byteswriter, METH_NOARGS, _Py_NULL}, 2547 | {"test_tuple", test_tuple, METH_NOARGS, _Py_NULL}, 2548 | {"test_try_incref", test_try_incref, METH_NOARGS, _Py_NULL}, 2549 | {_Py_NULL, _Py_NULL, 0, _Py_NULL} 2550 | }; 2551 | 2552 | 2553 | static int 2554 | module_exec(PyObject *module) 2555 | { 2556 | #ifdef __cplusplus 2557 | if (PyModule_AddIntMacro(module, __cplusplus)) { 2558 | return -1; 2559 | } 2560 | #endif 2561 | if (PyModule_AddStringMacro(module, PY_VERSION)) { 2562 | return -1; 2563 | } 2564 | if (PyModule_AddIntMacro(module, PY_VERSION_HEX)) { 2565 | return -1; 2566 | } 2567 | #ifdef PYPY_VERSION 2568 | if (PyModule_AddStringMacro(module, PYPY_VERSION)) { 2569 | return -1; 2570 | } 2571 | #endif 2572 | #ifdef PYPY_VERSION_NUM 2573 | if (PyModule_AddIntMacro(module, PYPY_VERSION_NUM)) { 2574 | return -1; 2575 | } 2576 | #endif 2577 | TryIncrefType.tp_name = "TryIncrefType"; 2578 | TryIncrefType.tp_basicsize = sizeof(PyObject); 2579 | TryIncrefType.tp_dealloc = TryIncref_dealloc; 2580 | TryIncrefType.tp_free = PyObject_Del; 2581 | if (PyType_Ready(&TryIncrefType) < 0) { 2582 | return -1; 2583 | } 2584 | return 0; 2585 | } 2586 | 2587 | 2588 | #if PY_VERSION_HEX >= 0x03050000 2589 | static PyModuleDef_Slot module_slots[] = { 2590 | {Py_mod_exec, _Py_CAST(void*, module_exec)}, 2591 | {0, _Py_NULL} 2592 | }; 2593 | #endif 2594 | 2595 | 2596 | #ifdef PYTHON3 2597 | static struct PyModuleDef module_def = { 2598 | PyModuleDef_HEAD_INIT, 2599 | MODULE_NAME_STR, // m_name 2600 | _Py_NULL, // m_doc 2601 | 0, // m_size 2602 | methods, // m_methods 2603 | #if PY_VERSION_HEX >= 0x03050000 2604 | module_slots, // m_slots 2605 | #else 2606 | _Py_NULL, // m_reload 2607 | #endif 2608 | _Py_NULL, // m_traverse 2609 | _Py_NULL, // m_clear 2610 | _Py_NULL, // m_free 2611 | }; 2612 | 2613 | 2614 | #define INIT_FUNC CONCAT(PyInit_, MODULE_NAME) 2615 | 2616 | #if PY_VERSION_HEX >= 0x03050000 2617 | PyMODINIT_FUNC 2618 | INIT_FUNC(void) 2619 | { 2620 | return PyModuleDef_Init(&module_def); 2621 | } 2622 | #else 2623 | // Python 3.4 2624 | PyMODINIT_FUNC 2625 | INIT_FUNC(void) 2626 | { 2627 | PyObject *module = PyModule_Create(&module_def); 2628 | if (module == _Py_NULL) { 2629 | return _Py_NULL; 2630 | } 2631 | if (module_exec(module) < 0) { 2632 | Py_DECREF(module); 2633 | return _Py_NULL; 2634 | } 2635 | return module; 2636 | } 2637 | #endif 2638 | 2639 | #else 2640 | // Python 2 2641 | 2642 | #define INIT_FUNC CONCAT(init, MODULE_NAME) 2643 | 2644 | PyMODINIT_FUNC 2645 | INIT_FUNC(void) 2646 | { 2647 | PyObject *module; 2648 | module = Py_InitModule4(MODULE_NAME_STR, 2649 | methods, 2650 | _Py_NULL, 2651 | _Py_NULL, 2652 | PYTHON_API_VERSION); 2653 | if (module == _Py_NULL) { 2654 | return; 2655 | } 2656 | 2657 | if (module_exec(module) < 0) { 2658 | return; 2659 | } 2660 | } 2661 | #endif 2662 | --------------------------------------------------------------------------------