{"id":3482,"library":"extension-helpers","title":"extension-helpers","description":"extension-helpers provides a suite of utilities designed to simplify the process of building and installing Python packages that include compiled extensions (e.g., C, C++, Fortran). It helps manage compiler flags, OpenMP support, and the Python Limited API (PEP 384) for ABI compatibility. The current version is 1.4.0, and it follows a stable release cadence with minor versions released every few months, often coinciding with Astropy-related ecosystem updates.","status":"active","version":"1.4.0","language":"en","source_language":"en","source_url":"https://github.com/astropy/extension-helpers","tags":["build-system","extensions","setuptools","C/C++","fortran","ABI"],"install":[{"cmd":"pip install extension-helpers","lang":"bash","label":"Install extension-helpers"}],"dependencies":[],"imports":[{"symbol":"get_extensions","correct":"from extension_helpers import get_extensions"},{"symbol":"add_extension_helpers_build_options","correct":"from extension_helpers import add_extension_helpers_build_options"}],"quickstart":{"code":"import os\nimport shutil\nfrom setuptools import Extension # setuptools must be installed\nfrom extension_helpers import add_extension_helpers_build_options\n\n# Ensure 'src' directory exists for the dummy C file\nos.makedirs(\"src\", exist_ok=True)\n\n# For demonstration, create a dummy C file\ndummy_c_file_path = os.path.join(\"src\", \"_dummy_ext.c\")\ndummy_c_file_content = \"\"\"\n#include <Python.h>\n\nstatic PyObject *\ndummy_func(PyObject *self, PyObject *args)\n{\n    Py_RETURN_NONE;\n}\n\nstatic PyMethodDef DummyMethods[] = {\n    {\"dummy_func\", dummy_func, METH_NOARGS, \"A dummy function.\"},\n    {NULL, NULL, 0, NULL}\n};\n\nstatic struct PyModuleDef dummymodule = {\n    PyModuleDef_HEAD_INIT,\n    \"_dummy_ext\",   /* name of module */\n    NULL, /* module documentation, may be NULL */\n    -1,       /* size of per-interpreter state of the module,\n                 or -1 if the module keeps state in global variables. */\n    DummyMethods\n};\n\nPyMODINIT_FUNC\nPyInit__dummy_ext(void)\n{\n    return PyModule_Create(&dummymodule);\n}\n\"\"\"\nwith open(dummy_c_file_path, \"w\") as f:\n    f.write(dummy_c_file_content)\n\n# Define a single extension\next = Extension(\n    'my_package._dummy_ext',\n    sources=[dummy_c_file_path],\n    include_dirs=[],\n    extra_compile_args=[],\n    extra_link_args=[]\n)\n\nprint(f\"Initial Extension: {ext.name}\")\nprint(f\"  Sources: {ext.sources}\")\nprint(f\"  Initial extra_compile_args: {ext.extra_compile_args}\")\n\n# Apply extension-helpers build options (e.g., for OpenMP, limited API)\n# This modifies the 'ext' object in-place\nadd_extension_helpers_build_options(ext)\n\nprint(f\"\\nExtension after add_extension_helpers_build_options:\")\nprint(f\"  Updated extra_compile_args: {ext.extra_compile_args}\")\nprint(f\"  Updated extra_link_args: {ext.extra_link_args}\")\n\n# Demonstrate the Limited API env var effect\nos.environ['EXTENSION_HELPERS_PY_LIMITED_API'] = 'true'\nprint(\"\\n--- Rerunning with EXTENSION_HELPERS_PY_LIMITED_API=true ---\")\n\next_limited = Extension(\n    'my_package._dummy_ext_limited',\n    sources=[dummy_c_file_path],\n    include_dirs=[],\n    extra_compile_args=[],\n    extra_link_args=[]\n)\nadd_extension_helpers_build_options(ext_limited)\nprint(f\"Extension (Limited API enabled): {ext_limited.name}\")\nprint(f\"  Updated extra_compile_args (Limited API): {ext_limited.extra_compile_args}\")\nprint(f\"  Updated extra_link_args (Limited API): {ext_limited.extra_link_args}\")\ndel os.environ['EXTENSION_HELPERS_PY_LIMITED_API']\n\n# Clean up the dummy C file and directory\nif os.path.exists(dummy_c_file_path):\n    os.remove(dummy_c_file_path)\nif os.path.exists(\"src\") and not os.listdir(\"src\"):\n    os.rmdir(\"src\")\nelif os.path.exists(\"src\"):\n    print(\"\\nNote: 'src' directory not removed as it contains other files.\")\n","lang":"python","description":"This quickstart demonstrates how to use `add_extension_helpers_build_options` to modify a `setuptools.Extension` object. It shows how the library injects compiler flags (e.g., for OpenMP if available) and how setting the `EXTENSION_HELPERS_PY_LIMITED_API` environment variable influences the compiler arguments for PEP 384 support. This code prepares the `Extension` object, but does not perform the actual compilation, which `setuptools` would handle in a `setup.py` or `pyproject.toml` based build."},"warnings":[{"fix":"Review C/C++ source code to ensure it only uses functions available in the Python Limited API. Test the built extension against multiple Python versions if targeting ABI compatibility.","message":"ABI Compatibility with Python Limited API (PEP 384) requires careful code review. While `EXTENSION_HELPERS_PY_LIMITED_API=true` (v1.4.0+) enables compiler flags for PEP 384, developers must ensure their C/C++ code adheres to the Limited API restrictions (e.g., avoiding private Python C API functions). Incorrect usage can lead to runtime errors or subtle bugs when the extension is used with different Python versions than it was built with.","severity":"gotcha","affected_versions":">=1.4.0"},{"fix":"Ensure a compatible C/C++ compiler is installed and accessible in your system's PATH. For specific features like OpenMP, verify the necessary libraries are also installed. Refer to your operating system's documentation for installing build tools (e.g., `build-essential` on Debian/Ubuntu, Xcode Command Line Tools on macOS, Visual C++ Build Tools on Windows).","message":"extension-helpers relies on a correctly configured C/C++ compiler toolchain (e.g., GCC, Clang, MSVC) and potentially additional libraries like OpenMP. Build failures often stem from missing development tools, incorrect environment variables (e.g., `CC`, `CXX`, `LDFLAGS`), or incompatible compiler versions.","severity":"gotcha","affected_versions":"All"},{"fix":"For new projects, prefer configuring `extension-helpers` through `pyproject.toml` if possible. For complex or dynamic build logic, a `setup.py` importing `get_extensions()` or `add_extension_helpers_build_options()` directly might be necessary. Avoid redundant or conflicting configurations across `pyproject.toml` and `setup.py`.","message":"The library supports configuration via `[tool.extension-helpers]` in `pyproject.toml` (since v1.1.0) or by directly calling its functions within a `setup.py`. Mixing these approaches or misunderstanding their precedence can lead to unexpected build behavior, where extensions are not discovered or configured correctly.","severity":"gotcha","affected_versions":">=1.1.0"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}