Skip to content
Draft
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
28 changes: 17 additions & 11 deletions mamba_gator/envmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,17 +698,19 @@ async def pkg_depends(self, pkg: str) -> Dict[str, List[str]]:
Returns:
{"package": List[dependencies]}
"""
if not self.is_mamba():
resp = {}

ans = await self._execute(self.manager, "repoquery", "depends", "--json", pkg)
rcode, output = ans

if rcode != 0:
self.log.warning(
"Package manager '{}' does not support dependency query.".format(
"Package manager '{}' does not support dependency query. "
"Ensure conda-libmamba-solver is installed or use mamba.".format(
self.manager
)
)
return {pkg: None}

resp = {}
ans = await self._execute(self.manager, "repoquery", "depends", "--json", pkg)
_, output = ans
query = self._clean_conda_json(output)

if "error" not in query:
Expand All @@ -732,11 +734,15 @@ async def list_available(self) -> Dict[str, List[Dict[str, str]]]:
"with_description": bool # Whether we succeed in get some channeldata.json files
}
"""
if self.is_mamba():
ans = await self._execute(self.manager, "repoquery", "search", "*", "--json")
else:
ans = await self._execute(self.manager, "repoquery", "search", "*", "--json")
rcode, output = ans

if rcode != 0:
# Fall back to conda search for older conda versions
self.log.debug("repoquery not available, falling back to conda search")
print("Fall back to conda search since repoquery not available")
ans = await self._execute(self.manager, "search", "--json")
_, output = ans
_, output = ans

current_loop = tornado.ioloop.IOLoop.current()
data = await current_loop.run_in_executor(None, self._clean_conda_json, output)
Expand All @@ -760,7 +766,7 @@ def process_mamba_repoquery_output(data: Dict) -> Dict:

return data_

if self.is_mamba():
if rcode == 0:
data = await current_loop.run_in_executor(None, process_mamba_repoquery_output, data)

def format_packages(data: Dict) -> List:
Expand Down
70 changes: 52 additions & 18 deletions mamba_gator/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1122,22 +1122,28 @@ async def test_package_list_available(conda_fetch, wait_for_task):

if has_mamba:
dummy = {"result": {"pkgs": list(chain(*dummy.values()))}}

f.side_effect = [
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]
f.side_effect = [
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]
else:
f.side_effect = [
(1, "repoquery not available")
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]

response = await conda_fetch("packages", method="GET")
assert response.code == 202
location = response.headers.get("Location")
response = await wait_for_task(location)
assert response.code == 200

args, _ = f.call_args_list[0]
if has_mamba:
if has_mambe:
args, _ = f.call_args_list[0]
assert args[1:] == ("repoquery", "search", "*", "--json")
else:
args, _ = f.call_args_list[1]
assert args[1:] == ("search", "--json")

body = json.loads(response.body)
Expand Down Expand Up @@ -1308,6 +1314,16 @@ async def test_package_list_available_local_channel(conda_fetch, wait_for_task):

if has_mamba:
dummy = {"result": {"pkgs": list(chain(*dummy.values()))}}
f.side_effect = [
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]
else:
f.side_effect = [
(1, "repoquery not available")
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]

with tempfile.TemporaryDirectory() as local_channel:
with open(os.path.join(local_channel, "channeldata.json"), "w+") as d:
Expand Down Expand Up @@ -1353,10 +1369,11 @@ async def test_package_list_available_local_channel(conda_fetch, wait_for_task):
response = await wait_for_task(location)
assert response.code == 200

args, _ = f.call_args_list[0]
if has_mamba:
if has_mambe:
args, _ = f.call_args_list[0]
assert args[1:] == ("repoquery", "search", "*", "--json")
else:
args, _ = f.call_args_list[1]
assert args[1:] == ("search", "--json")

body = json.loads(response.body)
Expand Down Expand Up @@ -1527,6 +1544,16 @@ async def test_package_list_available_no_description(conda_fetch, wait_for_task)

if has_mamba:
dummy = {"result": {"pkgs": list(chain(*dummy.values()))}}
f.side_effect = [
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]
else:
f.side_effect = [
(1, "repoquery not available")
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]

with tempfile.TemporaryDirectory() as local_channel:
local_name = local_channel.strip("/")
Expand Down Expand Up @@ -1558,10 +1585,11 @@ async def test_package_list_available_no_description(conda_fetch, wait_for_task)
response = await wait_for_task(location)
assert response.code == 200

args, _ = f.call_args_list[0]
if has_mamba:
if has_mambe:
args, _ = f.call_args_list[0]
assert args[1:] == ("repoquery", "search", "*", "--json")
else:
args, _ = f.call_args_list[1]
assert args[1:] == ("search", "--json")

body = json.loads(response.body)
Expand Down Expand Up @@ -1758,11 +1786,16 @@ async def test_package_list_available_caching(conda_fetch, wait_for_task):

if has_mamba:
dummy = {"result": {"pkgs": list(chain(*dummy.values()))}}

f.side_effect = [
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]
f.side_effect = [
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]
else:
f.side_effect = [
(1, "repoquery not available")
(0, json.dumps(dummy)),
(0, json.dumps(channels)),
]

# First retrieval - no cache available
response = await conda_fetch("packages", method="GET")
Expand All @@ -1771,10 +1804,11 @@ async def test_package_list_available_caching(conda_fetch, wait_for_task):
response = await wait_for_task(location)
assert response.code == 200

args, _ = f.call_args_list[0]
if has_mamba:
if has_mambe:
args, _ = f.call_args_list[0]
assert args[1:] == ("repoquery", "search", "*", "--json")
else:
args, _ = f.call_args_list[1]
assert args[1:] == ("search", "--json")

expected = {
Expand Down
53 changes: 49 additions & 4 deletions mamba_gator/tests/test_manager.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,61 @@
from pathlib import Path

import pytest
import json
from packaging.version import Version, InvalidVersion
from unittest.mock import AsyncMock, patch

from mamba_gator.envmanager import EnvManager, parse_version

from .utils import has_mamba


def test_envmanager_manager():
EnvManager._manager_exe = None
manager = EnvManager("", None)
stem = Path(manager.manager).stem
assert stem in ("mamba", "conda"), f"Unexpected manager: {stem}"
assert manager.is_mamba() == (stem == "mamba")

FAKE_CONDA_SEARCH_OUTPUT = json.dumps({
"numpy": [
{
"build": "py39_0",
"build_number": 0,
"channel": "https://repo.anaconda.com/pkgs/main/osx-64",
"name": "numpy",
"platform": "osx-64",
"version": "1.21.0"
},
{
"build": "py39_1",
"build_number": 1,
"channel": "https://repo.anaconda.com/pkgs/main/osx-64",
"name": "numpy",
"platform": "osx-64",
"version": "1.22.0"
}
]
})

@pytest.mark.asyncio
async def test_list_available_conda_search_fallback():
"""Test that list_available works when repoquery fails and we fall back to conda search."""
manager = EnvManager("", None)
expected = "mamba" if has_mamba else "conda"
assert Path(manager.manager).stem == expected
async def mock_execute(cmd, *args):
if "repoquery" in args:
return (1, "") # repoquery fails
if "search" in args:
return (0, FAKE_CONDA_SEARCH_OUTPUT)
# Pass through for config/channels calls
return await original_execute(cmd, *args)
original_execute = manager._execute
with patch.object(manager, "_execute", side_effect=mock_execute):
result = await manager.list_available()
assert "packages" in result
pkgs = result["packages"]
assert len(pkgs) == 1
assert pkgs[0]["name"] == "numpy"
assert isinstance(pkgs[0]["version"], list)
assert "1.22.0" in pkgs[0]["version"]


def test_parse_r_style_version_with_underscore():
Expand Down Expand Up @@ -54,6 +98,7 @@ async def test_list_available_returns_valid_structure():
"""Test that list_available returns the expected data structure."""
manager = EnvManager("", None)
result = await manager.list_available()
print('@@@@@result', result)
assert "packages" in result
assert "with_description" in result

Expand Down
8 changes: 4 additions & 4 deletions packages/common/src/components/PkgGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,16 @@ export class PkgGraph extends React.Component<IPkgGraphProps, IPkgGraphState> {
// Manager does not support dependency query
error = (
<span>
Please install{' '}
Dependenct query not supported. Ensure{' '}
<a
style={{ textDecoration: 'underline' }}
href="https://github.com/mamba-org/mamba"
href="https://conda.github.io/conda-libmamba-solver/"
rel="noreferrer"
target="_blank"
>
mamba
conda-libmamba-solver
</a>{' '}
manager to resolve dependencies.
is installed or use mamba.
</span>
);
} else if (
Expand Down
Loading