From a2d19c0234866dc9d4d55abf3009699c258bb72f Mon Sep 17 00:00:00 2001 From: Matt Davis <6775756+nitzmahone@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:24:15 -0700 Subject: [PATCH] Conditional support for Cython3.x, CI updates (#808) * Cython 3.x support needed for Python 3.13 * Move CI to a dynamic matrix * Use GHA-hosted Apple Silicon runners * Move Windows builds to cibuildwheel * Implement rudimentary PEP517 config-settings passthru to setuptools with custom in-tree setuptools build backend shim (blocked Windows build being moved to cibuildwheel). * Use build_config.toml to smuggle JSON to cibuildwheel, since it trashes JSON via CLI when doing containerized builds. --- .github/actions/dynamatrix/action.yml | 22 + .../actions/dynamatrix/matrix_yaml_to_json.py | 71 +++ .github/workflows/ci.yaml | 596 +++++++++++------- .github/workflows/manual_artifact_build.yaml | 446 ------------- MANIFEST.in | 1 + packaging/_pyyaml_pep517.py | 51 ++ pyproject.toml | 10 +- setup.py | 43 +- tests/legacy_tests/conftest.py | 6 +- tests/legacy_tests/test_appliance.py | 5 +- 10 files changed, 577 insertions(+), 674 deletions(-) create mode 100644 .github/actions/dynamatrix/action.yml create mode 100644 .github/actions/dynamatrix/matrix_yaml_to_json.py delete mode 100644 .github/workflows/manual_artifact_build.yaml create mode 100644 packaging/_pyyaml_pep517.py diff --git a/packaging/_pyyaml_pep517.py b/packaging/_pyyaml_pep517.py new file mode 100644 index 00000000..5f390266 --- /dev/null +++ b/packaging/_pyyaml_pep517.py @@ -0,0 +1,51 @@ +import inspect + + +def _bridge_build_meta(): + import functools + import sys + + from setuptools import build_meta + + self_module = sys.modules[__name__] + + for attr_name in build_meta.__all__: + attr_value = getattr(build_meta, attr_name) + if callable(attr_value): + setattr(self_module, attr_name, functools.partial(_expose_config_settings, attr_value)) + + +class ActiveConfigSettings: + _current = {} + + def __init__(self, config_settings): + self._config = config_settings + + def __enter__(self): + type(self)._current = self._config + + def __exit__(self, exc_type, exc_val, exc_tb): + type(self)._current = {} + + @classmethod + def current(cls): + return cls._current + + +def _expose_config_settings(real_method, *args, **kwargs): + from contextlib import nullcontext + import inspect + + sig = inspect.signature(real_method) + boundargs = sig.bind(*args, **kwargs) + + config = boundargs.arguments.get('config_settings') + + ctx = ActiveConfigSettings(config) if config else nullcontext() + + with ctx: + return real_method(*args, **kwargs) + + +_bridge_build_meta() + diff --git a/pyproject.toml b/pyproject.toml index 4bc04c0d..d8e5b969 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,9 @@ [build-system] -requires = ["setuptools", "wheel", "Cython<3.0"] -build-backend = "setuptools.build_meta" +requires = [ + "setuptools", # FIXME: declare min/max setuptools versions? + "wheel", + "Cython; python_version < '3.13'", + "Cython>=3.0; python_version >= '3.13'" +] +backend-path = ["packaging"] +build-backend = "_pyyaml_pep517" diff --git a/setup.py b/setup.py index c9b5dc6f..84bc2e46 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,8 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", @@ -82,7 +84,12 @@ with_cython = True try: from Cython.Distutils.extension import Extension as _Extension - from Cython.Distutils import build_ext as _build_ext + try: + # try old_build_ext from Cython > 3 first, until we can dump it entirely + from Cython.Distutils.old_build_ext import old_build_ext as _build_ext + except ImportError: + # Cython < 3 + from Cython.Distutils import build_ext as _build_ext with_cython = True except ImportError: if with_cython: @@ -94,6 +101,14 @@ bdist_wheel = None +try: + from _pyyaml_pep517 import ActiveConfigSettings +except ImportError: + class ActiveConfigSettings: + @staticmethod + def current(): + return {} + # on Windows, disable wheel generation warning noise windows_ignore_warnings = [ "Unknown distribution option: 'python_requires'", @@ -173,6 +188,31 @@ def __init__(self, name, sources, feature_name, feature_description, class build_ext(_build_ext): + def finalize_options(self): + super().finalize_options() + pep517_config = ActiveConfigSettings.current() + + build_config = pep517_config.get('pyyaml_build_config') + + if build_config: + import json + build_config = json.loads(build_config) + print(f"`pyyaml_build_config`: {build_config}") + else: + build_config = {} + print("No `pyyaml_build_config` setting found.") + + for key, value in build_config.items(): + existing_value = getattr(self, key, ...) + if existing_value is ...: + print(f"ignoring unknown config key {key!r}") + continue + + if existing_value: + print(f"combining {key!r} {existing_value!r} and {value!r}") + value = existing_value + value # FIXME: handle type diff + + setattr(self, key, value) def run(self): optional = True @@ -230,6 +270,7 @@ def build_extensions(self): if with_ext is not None and not with_ext: continue if with_cython: + print(f"BUILDING CYTHON EXT; {self.include_dirs=} {self.library_dirs=} {self.define=}") ext.sources = self.cython_sources(ext.sources, ext) try: self.build_extension(ext)