mamba/micromamba/tests/test_config.py

827 lines
30 KiB
Python

import os
import platform
import shutil
import subprocess
import textwrap
from pathlib import Path
import pytest
import yaml
from . import helpers
@pytest.fixture
def user_config_dir(tmp_home: Path):
"""Location of config files that are generated from mamba"""
maybe_xdg_config = os.getenv("XDG_CONFIG_DIR", "")
if maybe_xdg_config:
yield Path(maybe_xdg_config)
system = platform.system()
if system == "Linux" or system == "Darwin":
yield tmp_home / ".config/mamba"
elif system == "Windows":
yield Path(os.environ["APPDATA"]) / "mamba"
else:
raise RuntimeError(f"Unsupported system {system}")
class Dumper(yaml.Dumper):
"""A YAML dumper to properly indent lists.
https://github.com/yaml/pyyaml/issues/234#issuecomment-765894586
"""
def increase_indent(self, flow=False, *args, **kwargs):
return super().increase_indent(flow=flow, indentless=False)
def config(*args):
umamba = helpers.get_umamba()
cmd = [umamba, "config"] + [arg for arg in args if arg]
res = helpers.subprocess_run(*cmd)
if "--json" in args:
j = yaml.load(res, yaml.FullLoader)
return j
return res.decode()
@pytest.fixture
def rc_file_args(request):
"""Parametrizable fixture to choose content of rc file as a dict."""
return getattr(request, "param", {})
@pytest.fixture
def rc_file_text(rc_file_args):
"""The content of the rc_file."""
return yaml.dump(rc_file_args, Dumper=Dumper)
@pytest.fixture
def rc_file(
request,
rc_file_text,
tmp_home,
tmp_root_prefix,
tmp_prefix,
tmp_path,
user_config_dir,
):
"""Parametrizable fixture to create an rc file at the desired location.
The file is created in an isolated folder and set as the prefix, root prefix, or
home folder.
"""
if hasattr(request, "param"):
where, rc_filename = request.param
if where == "home":
rc_file = tmp_home / rc_filename
elif where == "root_prefix":
rc_file = tmp_root_prefix / rc_filename
elif where == "prefix":
rc_file = tmp_prefix / rc_filename
elif where == "user_config_dir":
rc_file = user_config_dir / rc_filename
elif where == "env_set_xdg":
os.environ["XDG_CONFIG_HOME"] = str(tmp_home / "custom_xdg_config_dir")
rc_file = tmp_home / "custom_xdg_config_dir" / "mamba" / rc_filename
elif where == "absolute":
rc_file = Path(rc_filename)
else:
raise ValueError("Bad rc file location")
if rc_file.suffix == ".d":
rc_file = rc_file / "test.yaml"
else:
rc_file = tmp_path / "umamba/config.yaml"
rc_file.parent.mkdir(parents=True, exist_ok=True)
with open(rc_file, "w+") as f:
f.write(rc_file_text)
return rc_file
class TestConfig:
def test_config_empty(self, tmp_home):
assert "Configuration of micromamba" in config()
@pytest.mark.parametrize("quiet_flag", ["-q", "--quiet"])
def test_config_quiet(self, quiet_flag, tmp_home):
assert config(quiet_flag) == ""
class TestConfigSources:
@pytest.mark.parametrize(
"rc_file", (("home", "dummy.yaml"), ("home", ".mambarc")), indirect=True
)
@pytest.mark.parametrize("rc_file_args", ({"override_channels_enabled": True},), indirect=True)
@pytest.mark.parametrize("quiet_flag", ["-q", "--quiet"])
@pytest.mark.parametrize("norc", [False, True])
def test_config_sources(self, rc_file, quiet_flag, norc):
if norc:
with pytest.raises(subprocess.CalledProcessError):
config("sources", quiet_flag, "--rc-file", rc_file, "--no-rc")
else:
res = config("sources", quiet_flag, "--rc-file", rc_file)
rc_file_short = str(rc_file).replace(os.path.expanduser("~"), "~")
assert res.strip().splitlines() == (
f"Configuration files (by precedence order):\n{rc_file_short}".splitlines()
)
@pytest.mark.parametrize("quiet_flag", ["-q", "--quiet"])
@pytest.mark.parametrize("norc", [False, True])
def test_config_sources_empty(self, tmp_prefix, quiet_flag, norc):
if norc:
res = config("sources", quiet_flag, "--no-rc")
assert res.strip() == "Configuration files disabled by --no-rc flag"
else:
res = config("sources", quiet_flag)
assert res.startswith("Configuration files (by precedence order):")
# TODO: test system located sources?
@pytest.mark.parametrize(
"rc_file",
(
# "/etc/conda/.condarc",
# "/etc/conda/condarc",
# "/etc/conda/condarc.d/",
# "/etc/conda/.mambarc",
# "/var/lib/conda/.condarc",
# "/var/lib/conda/condarc",
# "/var/lib/conda/condarc.d/",
# "/var/lib/conda/.mambarc",
("user_config_dir", "mambarc"),
("env_set_xdg", "mambarc"),
("home", ".conda/.condarc"),
("home", ".conda/condarc"),
("home", ".conda/condarc.d"),
("home", ".condarc"),
("home", ".mambarc"),
("root_prefix", ".condarc"),
("root_prefix", "condarc"),
("root_prefix", "condarc.d"),
("root_prefix", ".mambarc"),
("prefix", ".condarc"),
("prefix", "condarc"),
("prefix", "condarc.d"),
("prefix", ".mambarc"),
),
indirect=True,
)
@pytest.mark.parametrize("rc_file_args", ({"override_channels_enabled": True},), indirect=True)
def test_config_rc_file(self, rc_file, tmp_env_name):
srcs = config("sources", "-n", tmp_env_name).strip().splitlines()
short_name = str(rc_file).replace(os.path.expanduser("~"), "~")
expected_srcs = f"Configuration files (by precedence order):\n{short_name}".splitlines()
assert srcs == expected_srcs
@pytest.mark.parametrize(
"rc_file",
[("home", "somefile.yml")],
indirect=True,
)
@pytest.mark.parametrize("rc_file_args", ({"override_channels_enabled": True},), indirect=True)
def test_config_expand_user(self, rc_file):
rc_file_short = str(rc_file).replace(os.path.expanduser("~"), "~")
res = config("sources", "--rc-file", rc_file)
assert (
res.strip().splitlines()
== f"Configuration files (by precedence order):\n{rc_file_short}".splitlines()
)
class TestConfigList:
@pytest.mark.parametrize("rc_file_args", ({"channels": ["channel1", "channel2"]},))
def test_list_with_rc(self, rc_file, rc_file_text):
assert (
config("list", "--no-env", "--rc-file", rc_file).splitlines()
== rc_file_text.splitlines()
)
def test_list_without_rc(self):
assert config("list", "--no-env", "--no-rc").splitlines()[:-1] == []
@pytest.mark.parametrize("source_flag", ["--sources", "-s"])
@pytest.mark.parametrize("rc_file_args", ({"channels": ["channel1", "channel2"]},))
def test_list_with_sources(self, rc_file, source_flag):
home_folder = os.path.expanduser("~")
src = f" # '{str(rc_file).replace(home_folder, '~')}'"
assert (
config("list", "--no-env", "--rc-file", rc_file, source_flag).splitlines()
== f"channels:\n - channel1{src}\n - channel2{src}\n".splitlines()
)
@pytest.mark.parametrize("source_flag", ["--sources", "-s"])
@pytest.mark.parametrize("rc_file_args", ({"custom_channels": {"key1": "value1"}},))
def test_list_map_with_sources(self, rc_file, source_flag):
home_folder = os.path.expanduser("~")
src = f" # '{str(rc_file).replace(home_folder, '~')}'"
out = config("list", "--no-env", "--rc-file", rc_file, source_flag)
assert f"key1: value1{src}" in out
@pytest.mark.parametrize("desc_flag", ["--descriptions", "-d"])
@pytest.mark.parametrize("rc_file_args", ({"channels": ["channel1", "channel2"]},))
def test_list_with_descriptions(self, rc_file, desc_flag):
assert (
config("list", "--no-env", "--rc-file", rc_file, desc_flag).splitlines()
== "# channels\n# Define the list of channels\nchannels:\n"
" - channel1\n - channel2\n".splitlines()
)
@pytest.mark.parametrize("desc_flag", ["--long-descriptions", "-l"])
@pytest.mark.parametrize("rc_file_args", ({"channels": ["channel1", "channel2"]},))
def test_list_with_long_descriptions(self, rc_file, desc_flag):
assert (
config("list", "--no-env", "--rc-file", rc_file, desc_flag).splitlines()
== "# channels\n# The list of channels where the packages will be searched for.\n"
"# Note that '-c local' allows using locally built packages.\n"
"# See also 'channel_priority'.\nchannels:\n - channel1\n - channel2\n".splitlines()
)
@pytest.mark.parametrize("group_flag", ["--groups", "-g"])
@pytest.mark.parametrize("rc_file_args", ({"channels": ["channel1", "channel2"]},))
def test_list_with_groups(self, rc_file, group_flag):
group = (
"# ######################################################\n"
"# # Channels Configuration #\n"
"# ######################################################\n\n"
)
assert (
config("list", "--no-env", "--rc-file", rc_file, "-d", group_flag).splitlines()
== f"{group}# channels\n# Define the list of channels\nchannels:\n"
" - channel1\n - channel2\n".splitlines()
)
def test_env_vars(self):
os.environ["MAMBA_OFFLINE"] = "true"
assert (
config("list", "offline", "--no-rc", "-s").splitlines()
== "offline: true # 'MAMBA_OFFLINE'".splitlines()
)
os.environ["MAMBA_OFFLINE"] = "false"
assert (
config("list", "offline", "--no-rc", "-s").splitlines()
== "offline: false # 'MAMBA_OFFLINE'".splitlines()
)
os.environ.pop("MAMBA_OFFLINE")
def test_no_env(self):
os.environ["MAMBA_OFFLINE"] = "false"
assert (
config("list", "offline", "--no-rc", "--no-env", "-s", "--offline").splitlines()
== "offline: true # 'CLI'".splitlines()
)
os.environ.pop("MAMBA_OFFLINE")
def test_precedence(self):
rc_dir = os.path.expanduser(os.path.join("~", "test_mamba", helpers.random_string()))
os.makedirs(rc_dir, exist_ok=True)
rc_file = os.path.join(rc_dir, ".mambarc")
short_rc_file = rc_file.replace(os.path.expanduser("~"), "~")
with open(rc_file, "w") as f:
f.write("offline: true")
try:
if "MAMBA_OFFLINE" in os.environ:
os.environ.pop("MAMBA_OFFLINE")
assert (
config("list", "offline", f"--rc-file={rc_file}", "-s").splitlines()
== f"offline: true # '{short_rc_file}'".splitlines()
)
os.environ["MAMBA_OFFLINE"] = "false"
assert (
config("list", "offline", "--no-rc", "-s").splitlines()
== "offline: false # 'MAMBA_OFFLINE'".splitlines()
)
assert (
config("list", "offline", f"--rc-file={rc_file}", "-s").splitlines()
== f"offline: false # 'MAMBA_OFFLINE' > '{short_rc_file}'".splitlines()
)
assert (
config("list", "offline", f"--rc-file={rc_file}", "-s", "--offline").splitlines()
== f"offline: true # 'CLI' > 'MAMBA_OFFLINE' > '{short_rc_file}'".splitlines()
)
assert (
config(
"list",
"offline",
f"--rc-file={rc_file}",
"--no-env",
"-s",
"--offline",
).splitlines()
== f"offline: true # 'CLI' > '{short_rc_file}'".splitlines()
)
assert (
config(
"list",
"offline",
"--no-rc",
"--no-env",
"-s",
"--offline",
).splitlines()
== "offline: true # 'CLI'".splitlines()
)
finally:
if "MAMBA_OFFLINE" in os.environ:
os.environ.pop("MAMBA_OFFLINE")
shutil.rmtree(os.path.expanduser(os.path.join("~", "test_mamba")))
# TODO: instead of "Key is not present in file" => "Key " + key + "is not present in file"
class TestConfigModifiers:
def test_file_set_single_input(self, rc_file):
config("set", "json", "true", "--file", rc_file)
assert config("get", "json", "--file", rc_file).splitlines() == "json: true".splitlines()
def test_file_set_change_key_value(self, rc_file):
config("set", "json", "true", "--file", rc_file)
config("set", "json", "false", "--file", rc_file)
assert config("get", "json", "--file", rc_file).splitlines() == "json: false".splitlines()
def test_file_set_invalit_input(self, rc_file):
assert (
config("set", "$%#@abc", "--file", rc_file).splitlines()
== "Key is invalid or more than one key was received".splitlines()
)
def test_file_set_multiple_inputs(self, rc_file):
assert (
config(
"set",
"json",
"true",
"clean_tarballs",
"true",
"--file",
rc_file,
).splitlines()
== "Key is invalid or more than one key was received".splitlines()
)
def test_file_remove_single_input(self, rc_file):
config("set", "json", "true", "--file", rc_file)
assert config("remove-key", "json", "--file", rc_file).splitlines() == []
def test_file_remove_non_existent_key(self, rc_file):
assert (
config("remove-key", "json", "--file", rc_file).splitlines()
== "Key is not present in file".splitlines()
)
def test_file_remove_invalid_key(self, rc_file):
assert (
config("remove-key", "^&*&^def", "--file", rc_file).splitlines()
== "Key is not present in file".splitlines()
)
def test_file_remove_vector(self, rc_file):
config("append", "channels", "flowers", "--file", rc_file)
config("remove-key", "channels", "--file", rc_file)
assert (
config("get", "channels", "--file", rc_file).splitlines()
== "Key is not present in file".splitlines()
)
def test_file_remove_vector_value(self, rc_file):
# Backward test compatibility: when an empty file exists, the formatting is different
rc_file.unlink()
config("append", "channels", "totoro", "--file", rc_file)
config("append", "channels", "haku", "--file", rc_file)
config("remove", "channels", "totoro", "--file", rc_file)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"channels:",
" - haku",
]
# TODO: This behavior should be fixed "channels: []"
def test_file_remove_vector_all_values(self, rc_file):
config("append", "channels", "haku", "--file", rc_file)
config("remove", "channels", "haku", "--file", rc_file)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"Key is not present in file"
]
def test_file_remove_vector_nonexistent_value(self, rc_file):
config("append", "channels", "haku", "--file", rc_file)
assert (
config(
"remove",
"channels",
"chihiro",
"--file",
rc_file,
).splitlines()
== "Key is not present in file".splitlines()
)
def test_file_remove_vector_multiple_values(self, rc_file):
config("append", "channels", "haku", "--file", rc_file)
assert (
config(
"remove",
"channels",
"haku",
"chihiro",
"--file",
rc_file,
).splitlines()
== "Only one value can be removed at a time".splitlines()
)
def test_file_append_single_input(self, rc_file):
# Backward test compatibility: when an empty file exists, the formatting is different
rc_file.unlink()
config("append", "channels", "flowers", "--file", rc_file)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"channels:",
" - flowers",
]
def test_file_append_multiple_inputs(self, rc_file):
with open(rc_file, "w") as f:
f.write("channels:\n - foo")
config(
"append",
"channels",
"condesc,mambesc",
"--file",
rc_file,
)
assert (
config("get", "channels", "--file", rc_file).splitlines()
== "channels:\n - foo\n - condesc\n - mambesc".splitlines()
)
def test_file_append_multiple_keys(self, rc_file):
with open(rc_file, "w") as f:
f.write("channels:\n - foo\ndefault_channels:\n - bar")
config(
"append",
"channels",
"condesc,mambesc",
"default_channels",
"condescd,mambescd",
"--file",
rc_file,
)
assert (
config("get", "channels", "--file", rc_file).splitlines()
== "channels:\n - foo\n - condesc\n - mambesc".splitlines()
)
assert (
config("get", "default_channels", "--file", rc_file).splitlines()
== "default_channels:\n - bar\n - condescd\n - mambescd".splitlines()
)
def test_file_append_invalid_input(self, rc_file):
with pytest.raises(subprocess.CalledProcessError):
config("append", "--file", rc_file)
with pytest.raises(subprocess.CalledProcessError):
config("append", "@#A321", "--file", rc_file)
with pytest.raises(subprocess.CalledProcessError):
config("append", "json", "true", "--file", rc_file)
with pytest.raises(subprocess.CalledProcessError):
config(
"append",
"channels",
"foo,bar",
"json",
"true",
"--file",
rc_file,
)
def test_file_prepend_single_input(self, rc_file):
# Backward test compatibility: when an empty file exists, the formatting is different
rc_file.unlink()
config("prepend", "channels", "flowers", "--file", rc_file)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"channels:",
" - flowers",
]
def test_file_prepend_multiple_inputs(self, rc_file):
with open(rc_file, "w") as f:
f.write("channels:\n - foo")
config(
"prepend",
"channels",
"condesc,mambesc",
"--file",
rc_file,
)
assert (
config("get", "channels", "--file", rc_file).splitlines()
== "channels:\n - condesc\n - mambesc\n - foo".splitlines()
)
def test_file_prepend_multiple_keys(self, rc_file):
with open(rc_file, "w") as f:
f.write("channels:\n - foo\ndefault_channels:\n - bar")
config(
"prepend",
"channels",
"condesc,mambesc",
"default_channels",
"condescd,mambescd",
"--file",
rc_file,
)
assert (
config("get", "channels", "--file", rc_file).splitlines()
== "channels:\n - condesc\n - mambesc\n - foo".splitlines()
)
assert (
config("get", "default_channels", "--file", rc_file).splitlines()
== "default_channels:\n - condescd\n - mambescd\n - bar".splitlines()
)
def test_file_prepend_invalid_input(self, rc_file):
with pytest.raises(subprocess.CalledProcessError):
config("prepend", "--file", rc_file)
with pytest.raises(subprocess.CalledProcessError):
config("prepend", "@#A321", "--file", rc_file)
with pytest.raises(subprocess.CalledProcessError):
config("prepend", "json", "true", "--file", rc_file)
with pytest.raises(subprocess.CalledProcessError):
config(
"prepend",
"channels",
"foo,bar",
"json",
"true",
"--file",
rc_file,
)
def test_file_append_and_prepend_inputs(self, rc_file):
# Backward test compatibility: when an empty file exists, the formatting is different
rc_file.unlink()
config("append", "channels", "flowers", "--file", rc_file)
config("prepend", "channels", "powers", "--file", rc_file)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"channels:",
" - powers",
" - flowers",
]
def test_file_set_and_append_inputs(self, rc_file):
# Backward test compatibility: when an empty file exists, the formatting is different
rc_file.unlink()
config("set", "experimental", "true", "--file", rc_file)
config("append", "channels", "gandalf", "--file", rc_file)
config("append", "channels", "legolas", "--file", rc_file)
assert (
config("get", "experimental", "--file", rc_file).splitlines()
== "experimental: true".splitlines()
)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"channels:",
" - gandalf",
" - legolas",
]
def test_file_set_and_prepend_inputs(self, rc_file):
# Backward test compatibility: when an empty file exists, the formatting is different
rc_file.unlink()
config("set", "experimental", "false", "--file", rc_file)
config("prepend", "channels", "zelda", "--file", rc_file)
config("prepend", "channels", "link", "--file", rc_file)
assert (
config("get", "experimental", "--file", rc_file).splitlines()
== "experimental: false".splitlines()
)
assert config("get", "channels", "--file", rc_file).splitlines() == [
"channels:",
" - link",
" - zelda",
]
def test_flag_env_set(self, rc_file):
config("set", "experimental", "false", "--env")
assert (
config("get", "experimental", "--env").splitlines()
== "experimental: false".splitlines()
)
def test_flag_env_file_remove_vector(self, rc_file):
config("prepend", "channels", "thinga-madjiga", "--env")
config("remove-key", "channels", "--env")
assert (
config("get", "channels", "--env").splitlines()
== "Key is not present in file".splitlines()
)
def test_flag_env_file_set_and_append_inputs(self, rc_file):
config("set", "local_repodata_ttl", "2", "--env")
config("append", "channels", "finn", "--env")
config("append", "channels", "jake", "--env")
assert (
config("get", "local_repodata_ttl", "--env").splitlines()
== "local_repodata_ttl: 2".splitlines()
)
assert config("get", "channels", "--env").splitlines() == [
"channels:",
" - finn",
" - jake",
]
class TestConfigExpandVars:
@staticmethod
def _roundtrip(rc_file_path, rc_contents):
rc_file_path.write_text(rc_contents)
return config("list", "--json", "--no-env", "--rc-file", rc_file_path)
@classmethod
def _roundtrip_attr(cls, rc_file_path, attr, config_expr):
return cls._roundtrip(rc_file_path, f"{attr}: {config_expr}")[attr]
@pytest.mark.parametrize("yaml_quote", ["", '"'])
def test_expandvars_conda(self, monkeypatch, tmpdir_factory, rc_file, yaml_quote):
"""
Environment variables should be expanded in settings that have expandvars=True.
Test copied from Conda.
"""
def _expandvars(attr, config_expr, env_value):
config_expr = config_expr.replace("'", yaml_quote)
monkeypatch.setenv("TEST_VAR", env_value)
return self._roundtrip_attr(rc_file, attr, config_expr)
ssl_verify = _expandvars("ssl_verify", "${TEST_VAR}", "yes")
assert ssl_verify
for attr, env_value in [
# Not supported by Micromamba
# ("client_ssl_cert", "foo"),
# ("client_ssl_cert_key", "foo"),
("channel_alias", "http://foo"),
]:
value = _expandvars(attr, "${TEST_VAR}", env_value)
assert value == env_value
for attr in [
# Not supported by Micromamba
# "migrated_custom_channels",
# "proxy_servers",
]:
value = _expandvars(attr, "{'x': '${TEST_VAR}'}", "foo")
assert value == {"x": "foo"}
for attr in [
"channels",
"default_channels",
]:
value = _expandvars(attr, "['${TEST_VAR}']", "foo")
assert value == ["foo"]
custom_channels = _expandvars("custom_channels", "{'x': '${TEST_VAR}'}", "http://foo")
assert custom_channels["x"] == "http://foo"
custom_multichannels = _expandvars(
"custom_multichannels", "{'x': ['${TEST_VAR}']}", "http://foo"
)
assert len(custom_multichannels["x"]) == 1
assert custom_multichannels["x"][0] == "http://foo"
envs_dirs = _expandvars("envs_dirs", "['${TEST_VAR}']", "/foo")
assert any("foo" in d for d in envs_dirs)
pkgs_dirs = _expandvars("pkgs_dirs", "['${TEST_VAR}']", "/foo")
assert any("foo" in d for d in pkgs_dirs)
@pytest.mark.parametrize(
"inp,outp",
[
# Tests copied from: cpython/Lib/test/test_genericpath.py
("$", "$"),
("$$", "$$"),
("foo", "foo"),
("${foo}bar1", "barbar1"),
("$[foo]bar", "$[foo]bar"),
("$bar bar", "$bar bar"),
("$?bar", "$?bar"),
("$foo}bar", "bar}bar"),
("${foo", "${foo"),
# Not supported by Micromamba
# ("${{foo}}", "baz1"),
# *(
# [
# ("%", "%"),
# ("foo", "foo"),
# ("$foo bar", "bar bar"),
# ("${foo}bar", "barbar"),
# ("$[foo]bar", "$[foo]bar"),
# ("$bar bar", "$bar bar"),
# ("$?bar", "$?bar"),
# ("$foo}bar", "bar}bar"),
# ("${foo", "${foo"),
# ("${{foo}}", "baz1}"),
# ("$foo$foo", "barbar"),
# ("$bar$bar", "$bar$bar"),
# ("%foo% bar", "bar bar"),
# ("%foo%bar", "barbar"),
# ("%foo%%foo%", "barbar"),
# ("%%foo%%foo%foo%", "%foo%foobar"),
# ("%?bar%", "%?bar%"),
# ("%foo%%bar", "bar%bar"),
# ("'%foo%'%bar", "'%foo%'%bar"),
# ("bar'%foo%", "bar'%foo%"),
# ("'$foo'$foo", "'$foo'bar"),
# ("'$foo$foo", "'$foo$foo"),
# ]
# if platform.system() == "Windows"
# else []
# ),
# Our tests:
("$bar$bar", "$bar$bar"),
("$foo$foo", "barbar"),
("$foo$$foo bar", "bar$bar bar"),
("$foo bar", "bar bar"),
],
)
@pytest.mark.parametrize("yaml_quote", ["", '"', "'"])
def test_expandvars_cpython(self, monkeypatch, rc_file, inp, outp, yaml_quote):
monkeypatch.setenv("foo", "bar", True)
monkeypatch.setenv("{foo", "baz1", True)
monkeypatch.setenv("{foo}", "baz2", True)
assert outp == self._roundtrip_attr(rc_file, "channel_alias", yaml_quote + inp + yaml_quote)
@pytest.mark.parametrize(
"inp,outp",
[
(
'x", "y',
[
"${x",
"y}",
],
),
("x\ny", ["${x y}"]),
],
)
def test_envsubst_yaml_mixup(self, monkeypatch, rc_file, inp, outp):
assert self._roundtrip_attr(rc_file, "channels", f'["${{{inp}}}"]') == outp
def test_envsubst_empty_var(self, monkeypatch, rc_file):
monkeypatch.setenv("foo", "", True)
# Windows does not support empty environment variables
expected = "${foo}" if platform.system() == "Windows" else ""
assert self._roundtrip_attr(rc_file, "channel_alias", "'${foo}'") == expected
def test_envsubst_windows_problem(self, monkeypatch, rc_file):
# Real-world problematic .condarc file
condarc = textwrap.dedent(
"""
channel_alias: https://xxxxxxxxxxxxxxxxxxxx.com/t/${CONDA_API_KEY}/get
channels:
- xxxxxxxxxxx
- yyyyyyyyyyyy
- conda-forge
custom_channels:
yyyyyyyyyyyy: https://${CONDA_CHANNEL_UPLOAD_USER}:${CONDA_CHANNEL_UPLOAD_PASSWORD}@xxxxxxxxxxxxxxx.com
custom_multichannels:
conda-forge:
- https://conda.anaconda.org/conda-forge
"""
)
monkeypatch.setenv("CONDA_API_KEY", "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk", True)
monkeypatch.setenv("CONDA_CHANNEL_UPLOAD_USER", "uuuuuuuuu", True)
monkeypatch.setenv("CONDA_CHANNEL_UPLOAD_PASSWORD", "pppppppppppppppppppp", True)
out = self._roundtrip(rc_file, condarc)
assert (
out["channel_alias"]
== "https://xxxxxxxxxxxxxxxxxxxx.com/t/kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk/get"
)
assert (
out["custom_channels"]["yyyyyyyyyyyy"]
== "https://uuuuuuuuu:pppppppppppppppppppp@xxxxxxxxxxxxxxx.com"
)