From fbbf0d2b87f8718b3589d13ebe3056fd61ee0890 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 5 May 2026 18:34:44 -0700 Subject: [PATCH 1/3] Migrate setup.cfg into pyproject.toml Move metadata, packages, entry points, and the toolchain extra into the [project] / [tool.setuptools.*] tables and delete setup.cfg. The version attr now points at relenv.common.__version__ directly so AST-based resolution doesn't have to import the package. Drop the [sdist] owner/group=root directive: it only affected tarball ownership metadata in `setup.py sdist`, which PEP 517 builds don't honor anyway. --- pyproject.toml | 25 +++++++++++++++++++++++++ setup.cfg | 30 ------------------------------ 2 files changed, 25 insertions(+), 30 deletions(-) delete mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index 0977a438..d20c4011 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,31 @@ requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" +[project] +name = "relenv" +dynamic = ["version", "readme"] + +[project.urls] +"Source Code" = "https://github.com/saltstack/relative-environment-for-python" +Documentation = "https://relenv.readthedocs.io/en/latest/" +Changelog = "https://relenv.readthedocs.io/en/latest/changelog.html" + +[project.scripts] +relenv = "relenv.__main__:main" + +[project.optional-dependencies] +toolchain = ["ppbt"] + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.dynamic] +version = {attr = "relenv.common.__version__"} +readme = {file = "README.md", content-type = "text/markdown"} + +[tool.setuptools.packages.find] +include = ["relenv*", "tests*"] + [tool.pytest.ini_options] minversion = "6.0" testpaths = [ diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a32990d9..00000000 --- a/setup.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2025 Broadcom. -# SPDX-License-Identifier: Apache-2.0 - -[metadata] -name = relenv -version = attr: relenv.__version__ -long_description = file: README.md -long_description_content_type = text/markdown -project_urls = - Source Code = https://github.com/saltstack/relative-environment-for-python - Documentation = https://relenv.readthedocs.io/en/latest/ - Changelog = https://relenv.readthedocs.io/en/latest/changelog.html - - -[options] -packages = find: -package_dir = - relenv = relenv -include_package_data = True - -[options.entry_points] -console_scripts = - relenv = relenv.__main__:main - -[options.extras_require] -toolchain = ppbt - -[sdist] -owner = root -group = root From a9d43cf01755827339583b8998ff3cd6e8898a70 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 5 May 2026 18:34:49 -0700 Subject: [PATCH 2/3] Remove dead Builder.copy and stale pyconfig-fix.md Builder.copy() has had no callers since c6ba44a ("Clean up cruft"); deleting it lets the unused Version import drop too. relenv-pyconfig-fix.md was a transient working note that should not have been committed. --- relenv-pyconfig-fix.md | 193 --------------------------------- relenv/build/common/builder.py | 30 ----- 2 files changed, 223 deletions(-) delete mode 100644 relenv-pyconfig-fix.md diff --git a/relenv-pyconfig-fix.md b/relenv-pyconfig-fix.md deleted file mode 100644 index 9b9f89fc..00000000 --- a/relenv-pyconfig-fix.md +++ /dev/null @@ -1,193 +0,0 @@ -# relenv: Windows onedir for Python 3.13+ is missing `pyconfig.h` - -## Repository / branch - -`saltstack/relenv`, current `main` (and all releases `v0.22.7`–`v0.22.8`). - -## Symptom - -Any C extension built against a relenv-produced Windows onedir for Python -3.13.x fails with: - -``` -D:\path\to\onedir\include\Python.h(14): fatal error C1083: - Cannot open include file: 'pyconfig.h': No such file or directory -``` - -Reproduced on the Salt CI run that triggered this report: - - -The failures show up while pip tries to build wheels for `psutil`, -`timelib`, `markupsafe._speedups`, etc. against a Salt onedir that uses -relenv's `3.13.13-amd64-win.tar.xz` / `3.13.13-x86-win.tar.xz`. - -## Direct evidence - -`tar tJf 3.13.13-amd64-win.tar.xz | grep -i pyconfig` → no matches. -`tar tJf 3.12.13-amd64-win.tar.xz | grep -i pyconfig` → `Include/pyconfig.h`. - -So the published 3.13.x Windows tarballs simply do not contain -`Include/pyconfig.h`. The 3.12.x and earlier tarballs do. - -## Root cause - -In `relenv/build/windows.py`, around the `build_python` step, the code -that populates `/Include` is: - -```python -shutil.copytree( - src=str(dirs.source / "Include"), - dst=str(dirs.prefix / "Include"), - dirs_exist_ok=True, -) -if "3.13" not in env["RELENV_PY_MAJOR_VERSION"]: - shutil.copy( - src=str(dirs.source / "PC" / "pyconfig.h"), - dst=str(dirs.prefix / "Include"), - ) -``` - -For Python ≤ 3.12 there is a checked-in `PC/pyconfig.h` in the CPython -source tree. The `shutil.copy` above places it next to `Python.h` in the -onedir — without it, `Python.h` cannot find the configuration macros. - -In Python 3.13 the layout changed: - -* `PC/pyconfig.h` was deleted from the source tree. -* `PC/pyconfig.h.in` is now a template. -* MSBuild generates the real `pyconfig.h` into the build output - directory (`PCbuild\\pyconfig.h`). - -Confirm via the upstream API: - -``` -gh api repos/python/cpython/contents/PC/pyconfig.h?ref=v3.13.13 # 404 -gh api repos/python/cpython/contents/PC?ref=v3.13.13 # only pyconfig.h.in -``` - -Commit `842b42eb` ("Attempt to fix 3.13 windows build", 2024-10-21) added -the `if "3.13" not in ...` guard to stop the failing `shutil.copy(.../PC/pyconfig.h)` -on 3.13 — but never replaced it with logic that copies the *generated* -`pyconfig.h` out of the build directory. The result is a tarball with -`Python.h` but no `pyconfig.h`, which is unusable for compiling C -extensions. - -CPython's own packaging script handles the same fork at -`PC/layout/main.py` (in 3.13.13): - -```python -pc = ns.source / "PC" -if (pc / "pyconfig.h.in").is_file(): - yield "include/pyconfig.h", ns.build / "pyconfig.h" # 3.13+ -else: - yield "include/pyconfig.h", pc / "pyconfig.h" # ≤3.12 -``` - -That's the model relenv should follow. - -## Fix (proposed) - -Replace the existing `if`-block in `relenv/build/windows.py` with one -that picks the source vs. build location based on whether `PC/pyconfig.h.in` -exists. Concretely: - -```python -shutil.copytree( - src=str(dirs.source / "Include"), - dst=str(dirs.prefix / "Include"), - dirs_exist_ok=True, -) - -# Locate pyconfig.h. Python <= 3.12 ships a checked-in PC/pyconfig.h. -# Python 3.13+ replaced that with PC/pyconfig.h.in and MSBuild generates -# the real header into the build output directory. Mirror the logic in -# CPython's PC/layout/main.py. -pc_dir = dirs.source / "PC" -if (pc_dir / "pyconfig.h.in").is_file(): - pyconfig_src = build_dir / "pyconfig.h" -else: - pyconfig_src = pc_dir / "pyconfig.h" - -if not pyconfig_src.is_file(): - raise RuntimeError( - f"Expected pyconfig.h at {pyconfig_src}; CPython build did not " - "produce it. Check that the MSBuild step ran successfully." - ) - -shutil.copy(src=str(pyconfig_src), dst=str(dirs.prefix / "Include")) -``` - -Notes for the implementing agent: - -* `build_dir` is already in scope a few lines below — it is the variable - used to locate `python3.lib` / `python.lib` / `*.pyd` / `*.dll`. - Move the new code below the variable's definition or pass it in. -* The exact path inside the build output may need adjustment per arch - (`PCbuild\amd64\pyconfig.h` vs `PCbuild\win32\pyconfig.h`). On - inspection, MSBuild copies the final `pyconfig.h` to `$(BinaryOutputPath)`, - and other relenv code (e.g. the `*.pyd`/`*.dll` glob and `python3.lib` - copy) already references that same `build_dir`, so the same path is - the right one. Verify by listing `build_dir` for both arches mid-build. -* Do **not** restore the old unconditional copy from `PC/pyconfig.h` — - that path no longer exists on 3.13+ and will raise `FileNotFoundError`. -* Keep the `shutil.copytree(... / "Include", ...)` call as-is; the new - copy still needs to land *after* it so the generated header overwrites - any stale one inadvertently picked up. - -## Verification - -1. Build the affected onedirs locally: - ``` - relenv build --arch amd64 --python 3.13.13 - relenv build --arch x86 --python 3.13.13 - relenv build --arch amd64 --python 3.12.13 # regression check - ``` -2. Confirm `Include/pyconfig.h` exists in each resulting `` and - in the `*.tar.xz` produced by the `relenv-finalize` step: - ``` - tar tJf 3.13.13-amd64-win.tar.xz | grep -i pyconfig - tar tJf 3.13.13-x86-win.tar.xz | grep -i pyconfig - tar tJf 3.12.13-amd64-win.tar.xz | grep -i pyconfig - ``` - All three should print `Include/pyconfig.h`. -3. From the extracted onedir, build any C extension that includes - `Python.h`: - ``` - \Scripts\python.exe -m pip install --no-binary :all: psutil - ``` - This should succeed, which it currently does not on 3.13. - -## Suggested test - -Add a smoke check to relenv's CI that, for each Windows tarball produced, -asserts the presence of `Include/pyconfig.h`. A trivial post-build step: - -```python -import tarfile, sys -with tarfile.open(sys.argv[1]) as tf: - names = tf.getnames() -assert any(n.lower().endswith("include/pyconfig.h") for n in names), ( - f"{sys.argv[1]} is missing Include/pyconfig.h" -) -``` - -Run it against every `*-win.tar.xz` artifact. This would have caught the -regression introduced by `842b42eb` immediately. - -## Release / consumer impact - -* Salt's pyversion101 branch (Python 3.13.13) cannot produce a working - Windows onedir with relenv `0.22.7` or `0.22.8`. Once a relenv release - carrying this fix is cut (call it `0.22.9`), bump - `cicd/shared-gh-workflows-context.yml` `relenv_version` in salt and - re-run the onedir build job to confirm. -* No expected impact on Linux / macOS builds — the affected code path is - Windows-only and the equivalent POSIX builds already ship a generated - `pyconfig.h` correctly. - -## Out of scope - -* Same Salt CI run also shows a macOS arm64 onedir failure (`yaml.h - not found` while building PyYAML's C speedups, and a `cmake_minimum_required` - failure when pyzmq tries to build its bundled libzmq). Those are - separate problems and are **not** addressed by this fix. diff --git a/relenv/build/common/builder.py b/relenv/build/common/builder.py index 5565ac72..b24a642b 100644 --- a/relenv/build/common/builder.py +++ b/relenv/build/common/builder.py @@ -26,7 +26,6 @@ DATA_DIR, MODULE_DIR, ConfigurationError, - Version, WorkDirs, build_arch, extract_archive, @@ -279,35 +278,6 @@ def __init__( self.version = version self.set_arch(self.arch) - def copy(self, version: str, checksum: str | None) -> Builder: - """Create a copy of this Builder with a different version.""" - recipies: dict[str, Recipe] = {} - for name in self.recipies: - recipe = self.recipies[name] - recipies[name] = { - "build_func": recipe["build_func"], - "wait_on": list(recipe["wait_on"]), - "download": recipe["download"].copy() if recipe["download"] else None, - } - build = Builder( - self.root, - recipies, - self.build_default, - self.populate_env, - self.arch, - version, - ) - python_download = build.recipies["python"].get("download") - if python_download is None: - raise ConfigurationError("Python recipe is missing a download entry") - python_download.version = version - if checksum is None: - from relenv.pyversions import python_versions - - checksum = python_versions().get(Version(version)) - python_download.checksum = checksum - return build - def set_arch(self, arch: str) -> None: """ Set the architecture for the build. From 7d573b7ab535c0ed41947b39c29f1f6d35bb7e55 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 5 May 2026 18:34:56 -0700 Subject: [PATCH 3/3] Add wget retry flags to verify-build test buildscripts The OpenLDAP, libssh2, libgit2, cyrus-sasl, and curl downloads in test_install_pycurl, test_install_libgit2, and test_install_python_ldap failed in CI on a transient runner network blip. Pass --tries=10 --retry-connrefused --waitretry=10 --timeout=30 so each fetch survives a slow or intermittently unreachable upstream. --- tests/test_verify_build.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_verify_build.py b/tests/test_verify_build.py index 39e2b803..ab42d96f 100644 --- a/tests/test_verify_build.py +++ b/tests/test_verify_build.py @@ -1124,7 +1124,7 @@ def test_install_pycurl(pipexec, pyexec, build): buildscript = textwrap.dedent( """\ set -e - wget https://curl.se/download/curl-{curlver}.tar.gz + wget --tries=10 --retry-connrefused --waitretry=10 --timeout=30 https://curl.se/download/curl-{curlver}.tar.gz tar xvf curl-{curlver}.tar.gz cd curl-{curlver} source <({build}/bin/relenv buildenv) @@ -1220,7 +1220,7 @@ def test_install_libgit2(pipexec, build, minor_version, build_dir, versions): source <({build}/bin/relenv buildenv) # Build and install libssh2 - wget https://www.libssh2.org/download/libssh2-{libssh2}.tar.gz + wget --tries=10 --retry-connrefused --waitretry=10 --timeout=30 https://www.libssh2.org/download/libssh2-{libssh2}.tar.gz tar xvf libssh2-{libssh2}.tar.gz cd libssh2-{libssh2} mkdir bin @@ -1238,7 +1238,7 @@ def test_install_libgit2(pipexec, build, minor_version, build_dir, versions): cd ../.. # Build and install libgit2 - wget https://github.com/libgit2/libgit2/archive/refs/tags/v{libgit2}.tar.gz + wget --tries=10 --retry-connrefused --waitretry=10 --timeout=30 https://github.com/libgit2/libgit2/archive/refs/tags/v{libgit2}.tar.gz tar xvf v{libgit2}.tar.gz cd libgit2-{libgit2} mkdir build @@ -1294,7 +1294,7 @@ def test_install_python_ldap(pipexec, pyexec, build): source <({build}/bin/relenv buildenv) # Build and Install sasl - wget https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-{saslver}/cyrus-sasl-{saslver}.tar.gz + wget --tries=10 --retry-connrefused --waitretry=10 --timeout=30 https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-{saslver}/cyrus-sasl-{saslver}.tar.gz tar xvf cyrus-sasl-{saslver}.tar.gz cd cyrus-sasl-{saslver} ./configure --prefix=$RELENV_PATH @@ -1303,7 +1303,7 @@ def test_install_python_ldap(pipexec, pyexec, build): cd .. # Build and Install Open LDAP - wget https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-{ldapver}.tgz + wget --tries=10 --retry-connrefused --waitretry=10 --timeout=30 https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-{ldapver}.tgz tar xvf openldap-{ldapver}.tgz cd openldap-{ldapver} ./configure --prefix=$RELENV_PATH