Skip to content

Commit

Permalink
Platforms and channels are now defined by the most-downstream devenv …
Browse files Browse the repository at this point in the history
…file

Previously the `name`, `channels`, and `platforms` keys would only be overwritten by the root `devenv.yml` file (the one use to invoke `conda devenv` with).

Now any downstream `devenv.yml` file will override these keys, with the most downstream `devenv.yml` file "winning", which was always the intended behavior.
  • Loading branch information
nicoddemus committed Jul 22, 2024
1 parent 6a2c2ad commit e992457
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
CHANGELOG
=========

3.4.0 (UNRELEASED)
------------------

* Previously the ``name``, ``channels``, and ``platforms`` keys would only be overwritten by the root ``devenv.yml`` file (the one use to invoke ``conda devenv`` with).
Now any downstream ``devenv.yml`` file will override these keys, with the most downstream ``devenv.yml`` file "winning", which was always the intended behavior.

3.3.0 (2024-02-21)
------------------

Expand Down
10 changes: 6 additions & 4 deletions src/conda_devenv/devenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,12 @@ def load_yaml_dict(

merge_dependencies_version_specifications(merged_dict, key_to_merge="dependencies")

# Force these keys to always be set by the starting/root devenv file, when defined.
force_root_keys = ("name", "channels", "platforms")
forced_keys = {k: root_yaml[k] for k in force_root_keys if k in root_yaml}
merged_dict.update(forced_keys)
# Force these keys to always be set by the most downstream devenv file.
for forced_key in ("name", "channels", "platforms"):
for yaml_dict in all_yaml_dicts.values():
if forced_key in yaml_dict:
merged_dict[forced_key] = yaml_dict[forced_key]
break

if "environment" not in merged_dict:
merged_dict["environment"] = {}
Expand Down
213 changes: 209 additions & 4 deletions tests/test_load_yaml_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ def test_downstream_overrides_channels(tmp_path) -> None:
)
)

# This is the most downstream file which defines 'channels', so it overwrites any
# upstream definition.
b_fn = tmp_path / "b.devenv.yml"
b_fn.write_text(
textwrap.dedent(
Expand All @@ -150,13 +152,114 @@ def test_downstream_overrides_channels(tmp_path) -> None:
)
)

assert load_yaml_dict(b_fn) == {
"name": "b",
c_fn = tmp_path / "c.devenv.yml"
c_fn.write_text(
textwrap.dedent(
"""
name: c
includes:
- {{ root }}/b.devenv.yml
"""
)
)

assert load_yaml_dict(c_fn) == {
"name": "c",
"channels": ["b1_channel", "b2_channel"],
"environment": {},
}


def test_no_downstream_overrides_channels(tmp_path) -> None:
# The 'channels' key is defined only by one upstream file.
a_fn = tmp_path / "a.devenv.yml"
a_fn.write_text(
textwrap.dedent(
"""
name: a
channels:
- z_channel
- a_channel
"""
)
)

b_fn = tmp_path / "b.devenv.yml"
b_fn.write_text(
textwrap.dedent(
"""
name: b
includes:
- {{ root }}/a.devenv.yml
"""
)
)

c_fn = tmp_path / "c.devenv.yml"
c_fn.write_text(
textwrap.dedent(
"""
name: c
includes:
- {{ root }}/b.devenv.yml
"""
)
)

assert load_yaml_dict(c_fn) == {
"name": "c",
"channels": ["z_channel", "a_channel"],
"environment": {},
}


def test_root_overrides_channels(tmp_path) -> None:
a_fn = tmp_path / "a.devenv.yml"
a_fn.write_text(
textwrap.dedent(
"""
name: a
channels:
- z_channel
- a_channel
"""
)
)

b_fn = tmp_path / "b.devenv.yml"
b_fn.write_text(
textwrap.dedent(
"""
name: b
includes:
- {{ root }}/a.devenv.yml
"""
)
)

# This is the root file, so it overwrites the 'channels' completely.
c_fn = tmp_path / "c.devenv.yml"
c_fn.write_text(
textwrap.dedent(
"""
name: c
includes:
- {{ root }}/b.devenv.yml
channels:
- a_channel
- z_channel
- b_channel
"""
)
)

assert load_yaml_dict(c_fn) == {
"name": "c",
"channels": ["a_channel", "z_channel", "b_channel"],
"environment": {},
}


def test_downstream_overrides_platforms(tmp_path) -> None:
a_fn = tmp_path / "a.devenv.yml"
a_fn.write_text(
Expand All @@ -170,22 +273,124 @@ def test_downstream_overrides_platforms(tmp_path) -> None:
)
)

# This is the most downstream file which defines 'platforms', so it overwrites any
# upstream definition.
b_fn = tmp_path / "b.devenv.yml"
b_fn.write_text(
textwrap.dedent(
"""
name: b
includes:
- {{ root }}/a.devenv.yml
platforms:
- win-64
- osx-64
"""
)
)

c_fn = tmp_path / "c.devenv.yml"
c_fn.write_text(
textwrap.dedent(
"""
name: c
includes:
- {{ root }}/b.devenv.yml
"""
)
)

assert load_yaml_dict(c_fn) == {
"name": "c",
"platforms": ["win-64", "osx-64"],
"environment": {},
}


def test_no_downstream_overrides_platforms(tmp_path) -> None:
# The 'platforms' key is defined only by one upstream file.
a_fn = tmp_path / "a.devenv.yml"
a_fn.write_text(
textwrap.dedent(
"""
name: a
platforms:
- win-64
- linux-64
"""
)
)

b_fn = tmp_path / "b.devenv.yml"
b_fn.write_text(
textwrap.dedent(
"""
name: b
includes:
- {{ root }}/a.devenv.yml
"""
)
)

c_fn = tmp_path / "c.devenv.yml"
c_fn.write_text(
textwrap.dedent(
"""
name: c
includes:
- {{ root }}/b.devenv.yml
"""
)
)

assert load_yaml_dict(c_fn) == {
"name": "c",
"platforms": ["win-64", "linux-64"],
"environment": {},
}


def test_root_overrides_platforms(tmp_path) -> None:
a_fn = tmp_path / "a.devenv.yml"
a_fn.write_text(
textwrap.dedent(
"""
name: a
platforms:
- win-64
- linux-64
"""
)
)

b_fn = tmp_path / "b.devenv.yml"
b_fn.write_text(
textwrap.dedent(
"""
name: b
includes:
- {{ root }}/a.devenv.yml
"""
)
)

# This is the root file, so it overwrites the 'platforms' completely.
c_fn = tmp_path / "c.devenv.yml"
c_fn.write_text(
textwrap.dedent(
"""
name: c
includes:
- {{ root }}/b.devenv.yml
platforms:
- win-64
- osx-64
"""
)
)

assert load_yaml_dict(b_fn) == {
"name": "b",
assert load_yaml_dict(c_fn) == {
"name": "c",
"platforms": ["win-64", "osx-64"],
"environment": {},
}
Expand Down

0 comments on commit e992457

Please sign in to comment.