| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,2 +1,4 @@ | |||
| 1 | 1 | *.swp | |
| 2 | 2 | tests/build/ | |
| 3 | + *.py[cod] | ||
| 4 | + __pycache__ | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,11 +6,17 @@ Python C API compatibility | |||
| 6 | 6 | :alt: Build status of pyperf on Travis CI | |
| 7 | 7 | :target: https://travis-ci.com/github/pythoncapi/pythoncapi_compat | |
| 8 | 8 | ||
| 9 | - Header file providing new functions of the Python C API to Python 3.6. | ||
| 9 | + The Python C API compatibility project is made of two parts: | ||
| 10 | 10 | ||
| 11 | - Python 3.6 to Python 3.10 are supported. It requires a subset of C99 like | ||
| 12 | - ``static inline`` functions: | ||
| 13 | - see `PEP 7 <https://www.python.org/dev/peps/pep-0007/>`_. | ||
| 11 | + * ``pythoncapi_compat.h``: Header file providing new functions of the Python C | ||
| 12 | + API to Python 3.6. | ||
| 13 | + * ``upgrade_pythoncapi.py``: Script upgrading C extension modules to newer | ||
| 14 | + Python API without losing support for Python 3.6. It relies on | ||
| 15 | + ``pythoncapi_compat.h``. | ||
| 16 | + | ||
| 17 | + Python 3.6 to Python 3.10 are supported. A subset of C99 is required, like | ||
| 18 | + ``static inline`` functions: see `PEP 7 | ||
| 19 | + <https://www.python.org/dev/peps/pep-0007/>`_. | ||
| 14 | 20 | ||
| 15 | 21 | Homepage: | |
| 16 | 22 | https://github.com/pythoncapi/pythoncapi_compat | |
@@ -27,13 +33,114 @@ This project is covered by the `PSF Code of Conduct | |||
| 27 | 33 | Usage | |
| 28 | 34 | ===== | |
| 29 | 35 | ||
| 30 | - Copy the header file in your project and include it using:: | ||
| 36 | + Run upgrade_pythoncapi.py | ||
| 37 | + ------------------------- | ||
| 31 | 38 | ||
| 32 | - #include "pythoncapi_compat.h" | ||
| 39 | + Upgrade ``mod.c`` file:: | ||
| 33 | 40 | ||
| 41 | + python3 upgrade_pythoncapi.py mod.c | ||
| 34 | 42 | ||
| 35 | - Functions | ||
| 36 | - ========= | ||
| 43 | + Upgrade all ``.c`` and ``.h`` files of a project:: | ||
| 44 | + | ||
| 45 | + python3 upgrade_pythoncapi.py directory/ | ||
| 46 | + | ||
| 47 | + WARNING: files are modified in-place! If a file is modified, the original file | ||
| 48 | + is saved as ``<filename>.old``. | ||
| 49 | + | ||
| 50 | + To see command line options and list available operations, run it with no | ||
| 51 | + arguments:: | ||
| 52 | + | ||
| 53 | + python3 upgrade_pythoncapi.py | ||
| 54 | + | ||
| 55 | + For example, to only replace ``op->ob_type`` with ``Py_TYPE(op)``, use:: | ||
| 56 | + | ||
| 57 | + python3 upgrade_pythoncapi.py -o Py_TYPE mod.c | ||
| 58 | + | ||
| 59 | + Or the opposite, to apply all operations but leave ``op->ob_type`` unchanged, | ||
| 60 | + use:: | ||
| 61 | + | ||
| 62 | + python3 upgrade_pythoncapi.py -o all,-Py_TYPE mod.c | ||
| 63 | + | ||
| 64 | + Copy pythoncapi_compat.h | ||
| 65 | + ------------------------ | ||
| 66 | + | ||
| 67 | + Most upgrade_pythoncapi.py operations add ``#include "pythoncapi_compat.h"``. | ||
| 68 | + You may have to copy the ``pythoncapi_compat.h`` header file to your project. | ||
| 69 | + It can be copied from:: | ||
| 70 | + | ||
| 71 | + https://raw.githubusercontent.com/pythoncapi/pythoncapi_compat/master/pythoncapi_compat.h | ||
| 72 | + | ||
| 73 | + | ||
| 74 | + Upgrade Operations | ||
| 75 | + ================== | ||
| 76 | + | ||
| 77 | + ``upgrade_pythoncapi.py`` implements the following operations: | ||
| 78 | + | ||
| 79 | + * ``Py_TYPE``: | ||
| 80 | + | ||
| 81 | + * Replace ``op->ob_type`` with ``Py_TYPE(op)``. | ||
| 82 | + | ||
| 83 | + * ``Py_SIZE``: | ||
| 84 | + | ||
| 85 | + * Replace ``op->ob_size`` with ``Py_SIZE(op)``. | ||
| 86 | + | ||
| 87 | + * ``Py_REFCNT``: | ||
| 88 | + | ||
| 89 | + * Replace ``op->ob_refcnt`` with ``Py_REFCNT(op)``. | ||
| 90 | + | ||
| 91 | + * ``Py_SET_TYPE``: | ||
| 92 | + | ||
| 93 | + * Replace ``obj->ob_type = type;`` with ``Py_SET_TYPE(obj, type);``. | ||
| 94 | + * Replace ``Py_TYPE(obj) = type;`` with ``Py_SET_TYPE(obj, type);``. | ||
| 95 | + | ||
| 96 | + * ``Py_SET_SIZE``: | ||
| 97 | + | ||
| 98 | + * Replace ``obj->ob_size = size;`` with ``Py_SET_SIZE(obj, size);``. | ||
| 99 | + * Replace ``Py_SIZE(obj) = size;`` with ``Py_SET_SIZE(obj, size);``. | ||
| 100 | + | ||
| 101 | + * ``Py_SET_REFCNT``: | ||
| 102 | + | ||
| 103 | + * Replace ``obj->ob_refcnt = refcnt;`` with ``Py_SET_REFCNT(obj, refcnt);``. | ||
| 104 | + * Replace ``Py_REFCNT(obj) = refcnt;`` with ``Py_SET_REFCNT(obj, refcnt);``. | ||
| 105 | + | ||
| 106 | + * ``PyObject_NEW``: | ||
| 107 | + | ||
| 108 | + * Replace ``PyObject_NEW(...)`` with ``PyObject_New(...)``. | ||
| 109 | + * Replace ``PyObject_NEW_VAR(...)`` with ``PyObject_NewVar(...)``. | ||
| 110 | + | ||
| 111 | + * ``PyMem_MALLOC``: | ||
| 112 | + | ||
| 113 | + * Replace ``PyMem_MALLOC(n)`` with ``PyMem_Malloc(n)``. | ||
| 114 | + * Replace ``PyMem_REALLOC(ptr, n)`` with ``PyMem_Realloc(ptr, n)``. | ||
| 115 | + * Replace ``PyMem_FREE(ptr)``, ``PyMem_DEL(ptr)`` and ``PyMem_Del(ptr)`` . | ||
| 116 | + with ``PyMem_Free(n)``. | ||
| 117 | + | ||
| 118 | + * ``PyObject_MALLOC``: | ||
| 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 | + * Replace ``frame->f_back`` with ``_PyFrame_GetBackBorrow(frame)``. | ||
| 128 | + | ||
| 129 | + * ``PyFrame_GetCode``: | ||
| 130 | + | ||
| 131 | + * Replace ``frame->f_code`` with ``_PyFrame_GetCodeBorrow(frame)``. | ||
| 132 | + | ||
| 133 | + * ``PyThreadState_GetInterpreter``: | ||
| 134 | + | ||
| 135 | + * Replace ``tstate->interp`` with ``PyThreadState_GetInterpreter(tstate)``. | ||
| 136 | + | ||
| 137 | + * ``PyThreadState_GetFrame``: | ||
| 138 | + | ||
| 139 | + * Replace ``tstate->frame`` with ``_PyThreadState_GetFrameBorrow(tstate)``. | ||
| 140 | + | ||
| 141 | + | ||
| 142 | + pythoncapi_compat.h functions | ||
| 143 | + ============================= | ||
| 37 | 144 | ||
| 38 | 145 | Borrow variant | |
| 39 | 146 | -------------- | |
@@ -105,16 +212,38 @@ Run tests | |||
| 105 | 212 | ||
| 106 | 213 | Run the command:: | |
| 107 | 214 | ||
| 108 | - python3 tests/test_matrix.py | ||
| 109 | - # add -v option for verbose mode | ||
| 215 | + python3 runtests.py | ||
| 216 | + | ||
| 217 | + Verbose mode:: | ||
| 110 | 218 | ||
| 111 | - To test one specific Python executable:: | ||
| 219 | + python3 runtests.py -v | ||
| 112 | 220 | ||
| 113 | - python3.6 tests/run_tests.py | ||
| 114 | - # add -v option for verbose mode | ||
| 221 | + See tests in the ``tests/`` subdirectory. | ||
| 222 | + | ||
| 223 | + | ||
| 224 | + Links | ||
| 225 | + ===== | ||
| 115 | 226 | ||
| 227 | + * `pythoncapi_compat.h header | ||
| 228 | + <https://github.com/pythoncapi/pythoncapi_compat>`_: | ||
| 229 | + Header file providing new functions of the Python C API for old Python | ||
| 230 | + versions. | ||
| 231 | + * `Py_SET_TYPE() function documentation | ||
| 232 | + <https://docs.python.org/dev/c-api/structures.html#c.Py_SET_TYPE>`_ | ||
| 233 | + (Python 3.9) | ||
| 234 | + * `Py_SET_SIZE() function documentation | ||
| 235 | + <https://docs.python.org/dev/c-api/structures.html#c.Py_SET_SIZE>`_ | ||
| 236 | + (Python 3.9) | ||
| 237 | + * `Py_SET_REFCNT() function documentation | ||
| 238 | + <https://docs.python.org/dev/c-api/structures.html#c.Py_SET_REFCNT>`_ | ||
| 239 | + (Python 3.9) | ||
| 240 | + * `bpo-39573: [C API] Make PyObject an opaque structure in the limited C API | ||
| 241 | + <https://bugs.python.org/issue39573>`_ | ||
| 242 | + * `PEP 620 -- Hide implementation details from the C API | ||
| 243 | + <https://www.python.org/dev/peps/pep-0620/>`_ | ||
| 116 | 244 | ||
| 117 | 245 | Changelog | |
| 118 | 246 | ========= | |
| 119 | 247 | ||
| 120 | - * 2020-06-04: Creation of the project. | ||
| 248 | + * 2020-11-30: Creation of the upgrade_pythoncapi.py script. | ||
| 249 | + * 2020-06-04: Creation of the pythoncapi_compat.h header file. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,77 @@ | |||
| 1 | + #!/usr/bin/python3 | ||
| 2 | + """ | ||
| 3 | + Run the test suite on multiple Python versions. | ||
| 4 | + | ||
| 5 | + Usage:: | ||
| 6 | + | ||
| 7 | + python3 test_matrix.py | ||
| 8 | + python3 test_matrix.py -v # verbose mode | ||
| 9 | + """ | ||
| 10 | + import os.path | ||
| 11 | + import subprocess | ||
| 12 | + import sys | ||
| 13 | + import shutil | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + TEST_DIR = os.path.join(os.path.dirname(__file__), 'tests') | ||
| 17 | + TEST_COMPAT = os.path.join(TEST_DIR, "test_pythoncapi_compat.py") | ||
| 18 | + TEST_UPGRADE = os.path.join(TEST_DIR, "test_upgrade_pythoncapi.py") | ||
| 19 | + | ||
| 20 | + PYTHONS = ( | ||
| 21 | + "python3.6", | ||
| 22 | + "python3.7", | ||
| 23 | + "python3.8", | ||
| 24 | + "python3.9", | ||
| 25 | + "python3.10", | ||
| 26 | + "python3", | ||
| 27 | + "python3-debug", | ||
| 28 | + ) | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + def run_command(cmd): | ||
| 32 | + proc = subprocess.Popen(cmd) | ||
| 33 | + proc.wait() | ||
| 34 | + exitcode = proc.returncode | ||
| 35 | + if exitcode: | ||
| 36 | + sys.exit(exitcode) | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + def run_tests_exe(executable, verbose, tested): | ||
| 40 | + executable = os.path.realpath(executable) | ||
| 41 | + if executable in tested: | ||
| 42 | + return | ||
| 43 | + | ||
| 44 | + cmd = [executable, TEST_COMPAT] | ||
| 45 | + if verbose: | ||
| 46 | + cmd.append('-v') | ||
| 47 | + run_command(cmd) | ||
| 48 | + tested.add(executable) | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + def run_tests(python, verbose, tested): | ||
| 52 | + executable = shutil.which(python) | ||
| 53 | + if not executable: | ||
| 54 | + print(f"Ignore missing: {python}") | ||
| 55 | + return | ||
| 56 | + run_tests_exe(executable, verbose, tested) | ||
| 57 | + | ||
| 58 | + | ||
| 59 | + def main(): | ||
| 60 | + verbose = "-v" in sys.argv[1:] | ||
| 61 | + | ||
| 62 | + cmd = [sys.executable, TEST_UPGRADE] | ||
| 63 | + if verbose: | ||
| 64 | + cmd.append('-v') | ||
| 65 | + run_command(cmd) | ||
| 66 | + | ||
| 67 | + tested = set() | ||
| 68 | + for python in PYTHONS: | ||
| 69 | + run_tests(python, verbose, tested) | ||
| 70 | + run_tests_exe(sys.executable, verbose, tested) | ||
| 71 | + | ||
| 72 | + print() | ||
| 73 | + print(f"Tested: {len(tested)} Python executables") | ||
| 74 | + | ||
| 75 | + | ||
| 76 | + if __name__ == "__main__": | ||
| 77 | + main() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + set -e -x | ||
| 2 | + python3 tests/test_upgrade_pythoncapi.py -v | ||
| 3 | + python3 tests/test_matrix.py | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,8 +3,8 @@ | |||
| 3 | 3 | def main(): | |
| 4 | 4 | from distutils.core import setup, Extension | |
| 5 | 5 | ||
| 6 | - ext = Extension('test_pythoncapi_compat', | ||
| 7 | - sources=['test_pythoncapi_compat.c']) | ||
| 6 | + ext = Extension('test_pythoncapi_compat_cext', | ||
| 7 | + sources=['test_pythoncapi_compat_cext.c']) | ||
| 8 | 8 | ||
| 9 | 9 | setup(name="test_pythoncapi_compat", ext_modules=[ext]) | |
| 10 | 10 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,14 +54,16 @@ def build_ext(): | |||
| 54 | 54 | ||
| 55 | 55 | ||
| 56 | 56 | def import_tests(): | |
| 57 | - pythonpath = '' | ||
| 57 | + pythonpath = None | ||
| 58 | 58 | for name in os.listdir("build"): | |
| 59 | 59 | if name.startswith('lib.'): | |
| 60 | 60 | pythonpath = os.path.join("build", name) | |
| 61 | 61 | ||
| 62 | + if not pythonpath: | ||
| 63 | + raise Exception("Failed to find the build directory") | ||
| 62 | 64 | sys.path.append(pythonpath) | |
| 63 | - import test_pythoncapi_compat | ||
| 64 | - return test_pythoncapi_compat | ||
| 65 | + import test_pythoncapi_compat_cext | ||
| 66 | + return test_pythoncapi_compat_cext | ||
| 65 | 67 | ||
| 66 | 68 | ||
| 67 | 69 | def _run_tests(tests, verbose): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -231,13 +231,13 @@ static struct PyMethodDef methods[] = { | |||
| 231 | 231 | ||
| 232 | 232 | static struct PyModuleDef module = { | |
| 233 | 233 | PyModuleDef_HEAD_INIT, | |
| 234 | - .m_name = "test_pythoncapi_compat", | ||
| 234 | + .m_name = "test_pythoncapi_compat_cext", | ||
| 235 | 235 | .m_methods = methods, | |
| 236 | 236 | }; | |
| 237 | 237 | ||
| 238 | 238 | ||
| 239 | 239 | PyMODINIT_FUNC | |
| 240 | - PyInit_test_pythoncapi_compat(void) | ||
| 240 | + PyInit_test_pythoncapi_compat_cext(void) | ||
| 241 | 241 | { | |
| 242 | 242 | return PyModuleDef_Init(&module); | |
| 243 | 243 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments