Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions constructor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from .construct import parse as construct_parse
from .construct import verify as construct_verify
from .fcp import main as fcp_main
from .utils import StandaloneExe, identify_conda_exe, normalize_path, yield_lines
from .utils import StandaloneExe, check_version, identify_conda_exe, normalize_path, yield_lines

DEFAULT_CACHE_DIR = os.getenv("CONSTRUCTOR_CACHE", "~/.conda/constructor")

Expand Down Expand Up @@ -118,7 +118,7 @@ def main_build(
sys.exit("Error: micromamba is not supported on Windows installers.")

if info.get("uninstall_with_conda_exe") and not (
exe_type == StandaloneExe.CONDA and exe_version and exe_version >= Version("24.11.0")
exe_type == StandaloneExe.CONDA and check_version(exe_version, min_version="24.11.0")
):
sys.exit("Error: uninstalling with conda.exe requires conda-standalone 24.11.0 or newer.")

Expand Down Expand Up @@ -184,9 +184,7 @@ def main_build(
for path in info.get("extra_files", [])
)
and exe_type == StandaloneExe.CONDA
and exe_version
and exe_version >= Version("25.5.0")
and exe_version < Version("25.7.0")
and check_version(exe_version, min_version="25.5.0", max_version="25.7.0")
):
sys.exit(
"Error: installing with protected base environment requires conda-standalone newer than 25.5.x"
Expand Down Expand Up @@ -230,14 +228,14 @@ def main_build(
"Will assume it is compatible with shortcuts."
)
elif sys.platform != "win32" and (
exe_type != StandaloneExe.CONDA or (exe_version and exe_version < Version("23.11.0"))
exe_type != StandaloneExe.CONDA or check_version(exe_version, max_version="23.11.0")
):
logger.warning("conda-standalone 23.11.0 or above is required for shortcuts on Unix.")
info["_enable_shortcuts"] = "incompatible"

# Add --no-rc option to CONDA_EXE command so that existing
# .condarc files do not pollute the installation process.
if exe_type == StandaloneExe.CONDA and exe_version and exe_version >= Version("24.9.0"):
if exe_type == StandaloneExe.CONDA and check_version(exe_version, min_version="24.9.0"):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this may be a little out of scope but there's no logger warning or comment regarding the need to use 24.9.0 here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's out of scope. It's not really an opt-in feature but a bug fix. If conda didn't crash when using an unknown flag, none of these checks would even be necessary. A logger comment would be necessary if this was something a constructor user controlled, but couldn't have.

info["_ignore_condarcs_arg"] = "--no-rc"
elif exe_type == StandaloneExe.MAMBA:
info["_ignore_condarcs_arg"] = "--no-rc"
Expand Down
27 changes: 27 additions & 0 deletions constructor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@

from ruamel.yaml import YAML

try:
from conda.models.version import VersionOrder

has_conda_interface = True
except ImportError:
has_conda_interface = False

logger = logging.getLogger(__name__)
yaml = YAML(typ="rt")
yaml.default_flow_style = False
Expand Down Expand Up @@ -343,6 +350,26 @@ def identify_conda_exe(conda_exe: str | Path | None = None) -> tuple[StandaloneE
return None, None


def check_version(
exe_version: str | VersionOrder | None = None,
min_version: str | None = None,
max_version: str | None = None,
) -> bool:
"""Check if a version is within a version range.

The minimum version is assumed to be inclusive, the maximum version is not inclusive.
"""
if not exe_version or not has_conda_interface:
return False
if isinstance(exe_version, str):
exe_version = VersionOrder(exe_version)
if min_version and exe_version < VersionOrder(min_version):
return False
if max_version and exe_version >= VersionOrder(max_version):
return False
return True


def win_str_esc(s, newlines=True):
maps = [("$", "$$"), ('"', '$\\"'), ("\t", "$\\t")]
if newlines:
Expand Down
16 changes: 6 additions & 10 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from conda.models.version import VersionOrder as Version
from ruamel.yaml import YAML

from constructor.utils import StandaloneExe, identify_conda_exe
from constructor.utils import StandaloneExe, check_version, identify_conda_exe

if TYPE_CHECKING:
from collections.abc import Generator, Iterable
Expand Down Expand Up @@ -502,8 +502,7 @@ def test_example_mirrored_channels(tmp_path, request):
@pytest.mark.xfail(
(
CONDA_EXE == StandaloneExe.CONDA
and CONDA_EXE_VERSION is not None
and CONDA_EXE_VERSION < Version("23.11.0a0")
and not check_version(CONDA_EXE_VERSION, min_version="23.11.0a0")
),
reason="Known issue with conda-standalone<=23.10: shortcuts are created but not removed.",
)
Expand Down Expand Up @@ -683,8 +682,7 @@ def test_example_scripts(tmp_path, request):
@pytest.mark.skipif(
(
CONDA_EXE == StandaloneExe.MAMBA
or CONDA_EXE_VERSION is None
or CONDA_EXE_VERSION < Version("23.11.0a0")
and not check_version(CONDA_EXE_VERSION, min_version="23.11.0a0")
),
reason="menuinst v2 requires conda-standalone>=23.11.0; micromamba is not supported yet",
)
Expand Down Expand Up @@ -1208,7 +1206,7 @@ def _get_dacl_information(filepath: Path) -> dict:


@pytest.mark.xfail(
CONDA_EXE == StandaloneExe.CONDA and CONDA_EXE_VERSION < Version("24.9.0"),
CONDA_EXE == StandaloneExe.CONDA and not check_version(CONDA_EXE_VERSION, min_version="24.9.0"),
reason="Pre-existing .condarc breaks installation",
)
def test_ignore_condarc_files(tmp_path, monkeypatch, request):
Expand Down Expand Up @@ -1258,7 +1256,7 @@ def test_ignore_condarc_files(tmp_path, monkeypatch, request):


@pytest.mark.skipif(
CONDA_EXE == StandaloneExe.CONDA and CONDA_EXE_VERSION < Version("24.11.0"),
CONDA_EXE == StandaloneExe.CONDA and check_version(CONDA_EXE_VERSION, min_version="24.11.0"),
reason="Requires conda-standalone 24.11.x or newer",
)
@pytest.mark.skipif(not sys.platform == "win32", reason="Windows only")
Expand Down Expand Up @@ -1375,9 +1373,7 @@ def test_regressions(tmp_path, request):
@pytest.mark.xfail(
condition=(
CONDA_EXE == StandaloneExe.CONDA
and CONDA_EXE_VERSION
and CONDA_EXE_VERSION >= Version("25.5.0")
and CONDA_EXE_VERSION < Version("25.7.0")
and check_version(CONDA_EXE_VERSION, min_version="25.5.0", max_version="25.7.0")
),
reason="conda-standalone 25.5.x fails with protected base environments and older versions are ignored",
strict=True,
Expand Down
Loading