From ef9389227d6065edc9e45228a6fee0902a658e5f Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 6 Aug 2026 11:33:43 -0400 Subject: [PATCH 01/14] Add build environment to info.json --- constructor/conda_interface.py | 12 +++++ constructor/main.py | 4 +- tests/test_conda_interface.py | 92 ++++++++++++++++++++++++++++++++++ tests/test_examples.py | 8 +++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 tests/test_conda_interface.py diff --git a/constructor/conda_interface.py b/constructor/conda_interface.py index d40813165..05b0ab83d 100644 --- a/constructor/conda_interface.py +++ b/constructor/conda_interface.py @@ -202,3 +202,15 @@ def write_cache_dir(): cache_dir = join(PackageCacheData.first_writable().pkgs_dir, "cache") mkdir_p_sudo_safe(cache_dir) return cache_dir + + def get_build_env_records(prefix=None): + """Return the package records for the environment building the installer. + + Defaults to the currently active conda environment (`default_prefix`, + i.e. the one running constructor) if no prefix is given. Not to be + confused with construct.yaml's unrelated `default_prefix` setting, + which is the end user's install location. + """ + if prefix is None: + prefix = default_prefix + return list(PrefixData(prefix).iter_records()) diff --git a/constructor/main.py b/constructor/main.py index c73c07332..16aeb478c 100644 --- a/constructor/main.py +++ b/constructor/main.py @@ -25,7 +25,7 @@ from . import __version__ from ._schema import InstallerTypes from .build_outputs import process_build_outputs -from .conda_interface import SUPPORTED_PLATFORMS, cc_platform +from .conda_interface import SUPPORTED_PLATFORMS, cc_platform, get_build_env_records from .conda_interface import VersionOrder as Version from .construct import SCHEMA_PATH, ns_platform from .construct import parse as construct_parse @@ -279,6 +279,8 @@ def main_build( exe_version = Version(exe_version) info["_conda_exe_type"] = exe_type info["_conda_exe_version"] = exe_version + # Packages installed in the environment running constructor. + info["_build_environment_packages"] = get_build_env_records() if osname == "win" and exe_type == StandaloneExe.MAMBA: # TODO: Investigate errors on Windows and re-enable sys.exit("Error: micromamba is not supported on Windows installers.") diff --git a/tests/test_conda_interface.py b/tests/test_conda_interface.py new file mode 100644 index 000000000..9c0ac634a --- /dev/null +++ b/tests/test_conda_interface.py @@ -0,0 +1,92 @@ +import pytest +from conda.base.context import context +from conda.core.prefix_data import PrefixData +from conda.models.records import PackageRecord, PrefixRecord + +from constructor.conda_interface import get_build_env_records + +# Match the current platform, since tests run on multiple platforms +SUBDIR = context.subdir + + +def _make_package_record(name, version="1.2.3", build_number=0): + """Make a dummy package record for test fixtures.""" + return PackageRecord( + name=name, + version=version, + build=str(build_number), + build_number=build_number, + channel=None, + subdir=SUBDIR, + fn=f"{name}-{version}-{build_number}.conda", + ) + + +def _fake_prefix_data(tmp_path, records): + """Build a PrefixData whose in-memory records are injected directly, + so no conda-meta files are ever written to disk. Approach adapted from + conda/conda/testing/helpers.py::_get_solver_base, which patches the + same private `__prefix_records` attribute for the same reason.""" + prefix_data = PrefixData(str(tmp_path)) + prefix_data._PrefixData__prefix_records = { + rec.name: PrefixRecord.from_objects(rec) for rec in records + } + return prefix_data + + +@pytest.fixture +def patch_prefix_data(monkeypatch): + """Patch constructor.conda_interface.PrefixData so get_build_env_records() + returns records we control, without touching disk.""" + + def _patch(records): + # Replace PrefixData itself with this function, so calling + # PrefixData(prefix) returns our fake object instead of reading + # real conda-meta files. Reuse the same fake per prefix instead of + # building a new one each call. + fake_instances = {} + + def _fake_prefix_data_for(prefix): + if prefix not in fake_instances: + fake_instances[prefix] = _fake_prefix_data(prefix, records) + return fake_instances[prefix] + + monkeypatch.setattr("constructor.conda_interface.PrefixData", _fake_prefix_data_for) + + return _patch + + +@pytest.mark.parametrize( + "records", + [ + pytest.param([], id="empty-environment"), + pytest.param([_make_package_record("numpy")], id="single-package"), + pytest.param( + [ + _make_package_record("numpy"), + _make_package_record("conda-standalone", version="24.11.0"), + ], + id="multiple-packages", + ), + ], +) +def test_get_build_env_records_with_explicit_prefix(tmp_path, patch_prefix_data, records): + patch_prefix_data(records) + + result = get_build_env_records(prefix=str(tmp_path)) + + assert sorted(rec.name for rec in result) == sorted(rec.name for rec in records) + + +def test_get_build_env_records_defaults_to_active_environment( + monkeypatch, tmp_path, patch_prefix_data +): + """When prefix is not given, it must fall back to conda.exports.default_prefix + (the environment currently running constructor), not construct.yaml's + unrelated 'default_prefix' install-location setting.""" + monkeypatch.setattr("constructor.conda_interface.default_prefix", str(tmp_path)) + patch_prefix_data([_make_package_record("conda-standalone", version="24.11.0")]) + + result = get_build_env_records() + + assert [rec.name for rec in result] == ["conda-standalone"] diff --git a/tests/test_examples.py b/tests/test_examples.py index 5eb670200..e5826730d 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -2005,6 +2005,14 @@ def test_output_files(tmp_path, installer_type): # Test that info.json contains serialized objects info_json = json.loads((root_path / "info.json").read_text()) assert isinstance(info_json.get("_conda_exe_version"), str) + _build_environment_packages = info_json.get("_build_environment_packages") + assert isinstance(_build_environment_packages, list), ( + "Build environment packages is not a list." + ) + assert len(_build_environment_packages) > 0, "Build environment packages is empty." + assert isinstance(_build_environment_packages[0], dict), ( + "Build environment package not serialized." + ) _all_pkg_records = info_json.get("_all_pkg_records") assert isinstance(_all_pkg_records, list), "Package record is not a list." assert len(_all_pkg_records) > 0, "Package record is empty." From 1e2b28130c0148ce10f44195af1906253c5813cb Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 6 Aug 2026 13:48:55 -0400 Subject: [PATCH 02/14] Account for non-conda packages --- constructor/conda_interface.py | 3 ++- tests/test_conda_interface.py | 36 +++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/constructor/conda_interface.py b/constructor/conda_interface.py index 05b0ab83d..d3dd5210d 100644 --- a/constructor/conda_interface.py +++ b/constructor/conda_interface.py @@ -213,4 +213,5 @@ def get_build_env_records(prefix=None): """ if prefix is None: prefix = default_prefix - return list(PrefixData(prefix).iter_records()) + # interoperability=True also picks up pip-installed packages, not just conda ones. + return list(PrefixData(prefix, interoperability=True).iter_records()) diff --git a/tests/test_conda_interface.py b/tests/test_conda_interface.py index 9c0ac634a..b51f8ccbe 100644 --- a/tests/test_conda_interface.py +++ b/tests/test_conda_interface.py @@ -1,3 +1,5 @@ +import json + import pytest from conda.base.context import context from conda.core.prefix_data import PrefixData @@ -46,7 +48,7 @@ def _patch(records): # building a new one each call. fake_instances = {} - def _fake_prefix_data_for(prefix): + def _fake_prefix_data_for(prefix, **kwargs): if prefix not in fake_instances: fake_instances[prefix] = _fake_prefix_data(prefix, records) return fake_instances[prefix] @@ -90,3 +92,35 @@ def test_get_build_env_records_defaults_to_active_environment( result = get_build_env_records() assert [rec.name for rec in result] == ["conda-standalone"] + + +def test_get_build_env_records_includes_pip_installed_packages(tmp_path): + """Verify also pip packages are included among build environment deps.""" + meta_dir = tmp_path / "conda-meta" + meta_dir.mkdir() + + # Mock a conda package and a package installed via pip + python_record = PrefixRecord( + name="python", + version="1.2.3", + build="0", + build_number=0, + channel=None, + subdir=SUBDIR, + fn="python-1.2.3-0.conda", + paths_data={"paths": [], "paths_version": 1}, + files=[], + ) + (meta_dir / "python-1.2.3-0.json").write_text(json.dumps(python_record.dump())) + + site_packages = tmp_path / "lib" / "python1.2" / "site-packages" + site_packages.mkdir(parents=True) + dist_info = site_packages / "fakepkg-1.0.0.dist-info" + dist_info.mkdir() + (dist_info / "METADATA").write_text("Metadata-Version: 2.1\nName: fakepkg\nVersion: 1.0.0\n") + (dist_info / "INSTALLER").write_text("pip\n") + (dist_info / "RECORD").write_text("") + + result = get_build_env_records(prefix=str(tmp_path)) + + assert sorted(rec.name for rec in result) == ["fakepkg", "python"] From e3e1f71d413d6797c93f169170503ea5796834b1 Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 6 Aug 2026 13:52:07 -0400 Subject: [PATCH 03/14] Add news --- news/1314-build-environment-info | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 news/1314-build-environment-info diff --git a/news/1314-build-environment-info b/news/1314-build-environment-info new file mode 100644 index 000000000..f7cde7c0c --- /dev/null +++ b/news/1314-build-environment-info @@ -0,0 +1,19 @@ +### Enhancements + +* Add `_build_environment_packages` to `info.json`, listing the packages (including pip-installed ones) present in the environment used to build the installer. (#1314) + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* From 40bc9f3929da32a91258232bef2240f1692029f3 Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 6 Aug 2026 14:23:10 -0400 Subject: [PATCH 04/14] Fix test on windows --- tests/test_conda_interface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_conda_interface.py b/tests/test_conda_interface.py index b51f8ccbe..946eb0dab 100644 --- a/tests/test_conda_interface.py +++ b/tests/test_conda_interface.py @@ -2,6 +2,7 @@ import pytest from conda.base.context import context +from conda.common.path.python import get_python_site_packages_short_path from conda.core.prefix_data import PrefixData from conda.models.records import PackageRecord, PrefixRecord @@ -113,7 +114,7 @@ def test_get_build_env_records_includes_pip_installed_packages(tmp_path): ) (meta_dir / "python-1.2.3-0.json").write_text(json.dumps(python_record.dump())) - site_packages = tmp_path / "lib" / "python1.2" / "site-packages" + site_packages = tmp_path / get_python_site_packages_short_path("1.2") site_packages.mkdir(parents=True) dist_info = site_packages / "fakepkg-1.0.0.dist-info" dist_info.mkdir() From c45dfec82c2a5534ea9f3e1c779a4fa87283558d Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 6 Aug 2026 15:41:43 -0400 Subject: [PATCH 05/14] Rename some package names used in tests to use obvious dummy names --- tests/test_conda_interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_conda_interface.py b/tests/test_conda_interface.py index 946eb0dab..a582bd562 100644 --- a/tests/test_conda_interface.py +++ b/tests/test_conda_interface.py @@ -67,7 +67,7 @@ def _fake_prefix_data_for(prefix, **kwargs): pytest.param( [ _make_package_record("numpy"), - _make_package_record("conda-standalone", version="24.11.0"), + _make_package_record("foobar", version="24.11.0"), ], id="multiple-packages", ), @@ -88,11 +88,11 @@ def test_get_build_env_records_defaults_to_active_environment( (the environment currently running constructor), not construct.yaml's unrelated 'default_prefix' install-location setting.""" monkeypatch.setattr("constructor.conda_interface.default_prefix", str(tmp_path)) - patch_prefix_data([_make_package_record("conda-standalone", version="24.11.0")]) + patch_prefix_data([_make_package_record("foobar", version="24.11.0")]) result = get_build_env_records() - assert [rec.name for rec in result] == ["conda-standalone"] + assert [rec.name for rec in result] == ["foobar"] def test_get_build_env_records_includes_pip_installed_packages(tmp_path): From 80f9d1af513a4a44434a5ba7226d0825ab72e971 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 08:55:16 -0400 Subject: [PATCH 06/14] Move function call --- constructor/build_outputs.py | 4 +++- constructor/main.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/constructor/build_outputs.py b/constructor/build_outputs.py index efacdbe30..be238c4fb 100644 --- a/constructor/build_outputs.py +++ b/constructor/build_outputs.py @@ -16,7 +16,7 @@ from conda.core.prefix_data import PrefixGraph from . import __version__ -from .conda_interface import VersionOrder +from .conda_interface import VersionOrder, get_build_env_records logger = logging.getLogger(__name__) @@ -85,6 +85,8 @@ def _serialize(obj): else: return repr(obj) + # Packages installed in the environment running constructor. + info["_build_environment_packages"] = get_build_env_records() outpath = os.path.join(info["_output_dir"], "info.json") with open(outpath, "w") as f: json.dump(info, f, indent=2, default=_serialize) diff --git a/constructor/main.py b/constructor/main.py index 16aeb478c..c73c07332 100644 --- a/constructor/main.py +++ b/constructor/main.py @@ -25,7 +25,7 @@ from . import __version__ from ._schema import InstallerTypes from .build_outputs import process_build_outputs -from .conda_interface import SUPPORTED_PLATFORMS, cc_platform, get_build_env_records +from .conda_interface import SUPPORTED_PLATFORMS, cc_platform from .conda_interface import VersionOrder as Version from .construct import SCHEMA_PATH, ns_platform from .construct import parse as construct_parse @@ -279,8 +279,6 @@ def main_build( exe_version = Version(exe_version) info["_conda_exe_type"] = exe_type info["_conda_exe_version"] = exe_version - # Packages installed in the environment running constructor. - info["_build_environment_packages"] = get_build_env_records() if osname == "win" and exe_type == StandaloneExe.MAMBA: # TODO: Investigate errors on Windows and re-enable sys.exit("Error: micromamba is not supported on Windows installers.") From d4ee006e712350c61b1388a040d6a4f1ca125cbe Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 11:46:02 -0400 Subject: [PATCH 07/14] Remove codecov temporarily --- .github/workflows/main.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d56e23a94..9d0db8be8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -140,13 +140,7 @@ jobs: - name: Run unit tests run: | - pytest -ra -vvv --cov=constructor --cov-branch tests/ -m "not examples" - coverage run --branch --append -m constructor -V - coverage json - - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: unit + pytest -ra -vvv tests/ -m "not examples" - name: Run examples env: AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE: ${{ secrets.AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE }} @@ -157,15 +151,8 @@ jobs: CONSTRUCTOR_SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x86/signtool.exe" CONSTRUCTOR_VERBOSE: 0 run: | - rm -rf coverage.json - pytest -ra -vvv --cov=constructor --cov-branch tests/test_examples.py \ + pytest -ra -vvv tests/test_examples.py \ --splits $SPLIT_COUNT --group ${{ matrix.split-group }} - coverage run --branch --append -m constructor -V - coverage json - - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: integration - name: Check docs and schema are up-to-date if: matrix.config.check-docs-schema run: | From 70203c90b7f8b50a1643a6266d643302adf44e79 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 11:56:21 -0400 Subject: [PATCH 08/14] DEBUG: Test deselect new test file --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9d0db8be8..a53fe68a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -140,7 +140,7 @@ jobs: - name: Run unit tests run: | - pytest -ra -vvv tests/ -m "not examples" + pytest -ra -vvv tests/ -m "not examples" --ignore=tests/test_conda_interface.py - name: Run examples env: AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE: ${{ secrets.AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE }} From 9834845a10c757b8af1257ec26479241be594f8c Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 12:05:34 -0400 Subject: [PATCH 09/14] Test override pytest-split option --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a53fe68a4..4aef97bf6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -140,7 +140,7 @@ jobs: - name: Run unit tests run: | - pytest -ra -vvv tests/ -m "not examples" --ignore=tests/test_conda_interface.py + pytest -ra -vvv tests/ -m "not examples" -o addopts="" - name: Run examples env: AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE: ${{ secrets.AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE }} From accdd549b9b960ca8587b077bc7d20492d9e06e4 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 12:13:47 -0400 Subject: [PATCH 10/14] DEBUG: Temporarily disable call to new function --- constructor/build_outputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constructor/build_outputs.py b/constructor/build_outputs.py index be238c4fb..fdd654023 100644 --- a/constructor/build_outputs.py +++ b/constructor/build_outputs.py @@ -86,7 +86,7 @@ def _serialize(obj): return repr(obj) # Packages installed in the environment running constructor. - info["_build_environment_packages"] = get_build_env_records() + # info["_build_environment_packages"] = get_build_env_records() outpath = os.path.join(info["_output_dir"], "info.json") with open(outpath, "w") as f: json.dump(info, f, indent=2, default=_serialize) From 675f0e18c96c7e1fec86986b83a88c12c716593d Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 12:14:01 -0400 Subject: [PATCH 11/14] pre-commit --- constructor/build_outputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constructor/build_outputs.py b/constructor/build_outputs.py index fdd654023..ce79e555a 100644 --- a/constructor/build_outputs.py +++ b/constructor/build_outputs.py @@ -16,7 +16,7 @@ from conda.core.prefix_data import PrefixGraph from . import __version__ -from .conda_interface import VersionOrder, get_build_env_records +from .conda_interface import VersionOrder logger = logging.getLogger(__name__) From 8c38e5522f772d5e5091faf6d7bfe62791c8f7fe Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 12:17:33 -0400 Subject: [PATCH 12/14] Add back the --ignore since I disabled the function call earlier --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4aef97bf6..13e437939 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -140,7 +140,7 @@ jobs: - name: Run unit tests run: | - pytest -ra -vvv tests/ -m "not examples" -o addopts="" + pytest -ra -vvv tests/ -m "not examples" -o addopts="" --ignore=tests/test_conda_interface.py - name: Run examples env: AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE: ${{ secrets.AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE }} From f491774d3ec648b93f2f96977d9c26259d562635 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 12:45:59 -0400 Subject: [PATCH 13/14] Revert back to state before debugging started --- .github/workflows/main.yml | 17 +++++++++++++++-- constructor/build_outputs.py | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 13e437939..d56e23a94 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -140,7 +140,13 @@ jobs: - name: Run unit tests run: | - pytest -ra -vvv tests/ -m "not examples" -o addopts="" --ignore=tests/test_conda_interface.py + pytest -ra -vvv --cov=constructor --cov-branch tests/ -m "not examples" + coverage run --branch --append -m constructor -V + coverage json + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: unit - name: Run examples env: AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE: ${{ secrets.AZURE_SIGNTOOL_KEY_VAULT_CERTIFICATE }} @@ -151,8 +157,15 @@ jobs: CONSTRUCTOR_SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x86/signtool.exe" CONSTRUCTOR_VERBOSE: 0 run: | - pytest -ra -vvv tests/test_examples.py \ + rm -rf coverage.json + pytest -ra -vvv --cov=constructor --cov-branch tests/test_examples.py \ --splits $SPLIT_COUNT --group ${{ matrix.split-group }} + coverage run --branch --append -m constructor -V + coverage json + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: integration - name: Check docs and schema are up-to-date if: matrix.config.check-docs-schema run: | diff --git a/constructor/build_outputs.py b/constructor/build_outputs.py index ce79e555a..be238c4fb 100644 --- a/constructor/build_outputs.py +++ b/constructor/build_outputs.py @@ -16,7 +16,7 @@ from conda.core.prefix_data import PrefixGraph from . import __version__ -from .conda_interface import VersionOrder +from .conda_interface import VersionOrder, get_build_env_records logger = logging.getLogger(__name__) @@ -86,7 +86,7 @@ def _serialize(obj): return repr(obj) # Packages installed in the environment running constructor. - # info["_build_environment_packages"] = get_build_env_records() + info["_build_environment_packages"] = get_build_env_records() outpath = os.path.join(info["_output_dir"], "info.json") with open(outpath, "w") as f: json.dump(info, f, indent=2, default=_serialize) From 6a7b66bce6cce7c3ba1e15b40cb68751703e5c87 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 Aug 2026 15:18:19 -0400 Subject: [PATCH 14/14] Move function definition --- constructor/build_outputs.py | 19 +++++++++++++++++-- constructor/conda_interface.py | 13 ------------- tests/test_conda_interface.py | 8 ++++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/constructor/build_outputs.py b/constructor/build_outputs.py index be238c4fb..39a5c9738 100644 --- a/constructor/build_outputs.py +++ b/constructor/build_outputs.py @@ -13,14 +13,29 @@ from conda.base.constants import UNKNOWN_CHANNEL from conda.common.url import remove_auth, split_anaconda_token -from conda.core.prefix_data import PrefixGraph +from conda.core.prefix_data import PrefixData, PrefixGraph +from conda.exports import default_prefix from . import __version__ -from .conda_interface import VersionOrder, get_build_env_records +from .conda_interface import VersionOrder logger = logging.getLogger(__name__) +def get_build_env_records(prefix=None): + """Return the package records for the environment building the installer. + + Defaults to the currently active conda environment (`default_prefix`, + i.e. the one running constructor) if no prefix is given. Not to be + confused with construct.yaml's unrelated `default_prefix` setting, + which is the end user's install location. + """ + if prefix is None: + prefix = default_prefix + # interoperability=True also picks up pip-installed packages, not just conda ones. + return list(PrefixData(prefix, interoperability=True).iter_records()) + + def _validate_output(output): if isinstance(output, str): output = {output: None} diff --git a/constructor/conda_interface.py b/constructor/conda_interface.py index d3dd5210d..d40813165 100644 --- a/constructor/conda_interface.py +++ b/constructor/conda_interface.py @@ -202,16 +202,3 @@ def write_cache_dir(): cache_dir = join(PackageCacheData.first_writable().pkgs_dir, "cache") mkdir_p_sudo_safe(cache_dir) return cache_dir - - def get_build_env_records(prefix=None): - """Return the package records for the environment building the installer. - - Defaults to the currently active conda environment (`default_prefix`, - i.e. the one running constructor) if no prefix is given. Not to be - confused with construct.yaml's unrelated `default_prefix` setting, - which is the end user's install location. - """ - if prefix is None: - prefix = default_prefix - # interoperability=True also picks up pip-installed packages, not just conda ones. - return list(PrefixData(prefix, interoperability=True).iter_records()) diff --git a/tests/test_conda_interface.py b/tests/test_conda_interface.py index a582bd562..943bb066d 100644 --- a/tests/test_conda_interface.py +++ b/tests/test_conda_interface.py @@ -6,7 +6,7 @@ from conda.core.prefix_data import PrefixData from conda.models.records import PackageRecord, PrefixRecord -from constructor.conda_interface import get_build_env_records +from constructor.build_outputs import get_build_env_records # Match the current platform, since tests run on multiple platforms SUBDIR = context.subdir @@ -39,7 +39,7 @@ def _fake_prefix_data(tmp_path, records): @pytest.fixture def patch_prefix_data(monkeypatch): - """Patch constructor.conda_interface.PrefixData so get_build_env_records() + """Patch constructor.build_outputs.PrefixData so get_build_env_records() returns records we control, without touching disk.""" def _patch(records): @@ -54,7 +54,7 @@ def _fake_prefix_data_for(prefix, **kwargs): fake_instances[prefix] = _fake_prefix_data(prefix, records) return fake_instances[prefix] - monkeypatch.setattr("constructor.conda_interface.PrefixData", _fake_prefix_data_for) + monkeypatch.setattr("constructor.build_outputs.PrefixData", _fake_prefix_data_for) return _patch @@ -87,7 +87,7 @@ def test_get_build_env_records_defaults_to_active_environment( """When prefix is not given, it must fall back to conda.exports.default_prefix (the environment currently running constructor), not construct.yaml's unrelated 'default_prefix' install-location setting.""" - monkeypatch.setattr("constructor.conda_interface.default_prefix", str(tmp_path)) + monkeypatch.setattr("constructor.build_outputs.default_prefix", str(tmp_path)) patch_prefix_data([_make_package_record("foobar", version="24.11.0")]) result = get_build_env_records()