Skip to content

Commit 9c5226a

Browse files
committed
merge ManagerInfo in BaseManager
1 parent 1a219e1 commit 9c5226a

13 files changed

Lines changed: 205 additions & 202 deletions

File tree

src/ewoks/_requirements/managers/conda.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import logging
2+
import os
3+
import sys
4+
from typing import Optional
5+
from typing import Tuple
26

37
import yaml
48

@@ -11,6 +15,26 @@
1115

1216
class CondaManager(BaseManager):
1317
NAME = "conda"
18+
PRIORITY = 4
19+
20+
def __init__(self, *command: str) -> None:
21+
if not command:
22+
command = self._get_conda_command()
23+
super().__init__(*command)
24+
25+
def version(self) -> Optional[str]:
26+
"""Returns None when this manager is not available."""
27+
try:
28+
output = self._check_output("--version")
29+
except RuntimeError:
30+
return None
31+
return output.strip().split(" ")[-1]
32+
33+
def is_active(self) -> bool:
34+
"""Manager is explicitly active."""
35+
return "CONDA_PREFIX" in os.environ or os.path.exists(
36+
os.path.join(sys.prefix, "conda-meta")
37+
)
1438

1539
def _gather_requirements(self, manager_version: str) -> CondaRequirements:
1640
output = self._check_output("env", "export")
@@ -28,3 +52,16 @@ def install_requirements(self, requirements: CondaRequirements) -> None:
2852
text = yaml.safe_dump(requirements.environment)
2953
with self._temporary_file(text, ".yml") as tmp_path:
3054
self._check_call("env", "update", "-f", tmp_path)
55+
56+
def _get_conda_command(self) -> Tuple[str, ...]:
57+
try:
58+
_ = self._check_output_raw("mamba", "--version")
59+
return ("mamba",)
60+
except Exception:
61+
pass
62+
try:
63+
_ = self._check_output_raw("micromamba", "--version")
64+
return ("micromamba",)
65+
except Exception:
66+
pass
67+
return ("conda",)

src/ewoks/_requirements/managers/pip.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import importlib.metadata
12
import logging
3+
import sys
24
from typing import List
5+
from typing import Optional
36

47
from ..metadata import pip_freeze
58
from ..metadata.gather import gather_requirements
@@ -11,6 +14,23 @@
1114

1215
class PipManager(BaseManager):
1316
NAME = "pip"
17+
PRIORITY = 0
18+
19+
def __init__(self, *command: str) -> None:
20+
if not command:
21+
command = sys.executable, "-m", "pip"
22+
super().__init__(*command)
23+
24+
def version(self) -> Optional[str]:
25+
"""Returns None when this manager is not available."""
26+
try:
27+
return importlib.metadata.version("pip")
28+
except importlib.metadata.PackageNotFoundError:
29+
return None
30+
31+
def is_active(self) -> bool:
32+
"""Manager is explicitly active."""
33+
return False
1434

1535
def _gather_requirements(self, manager_version: str) -> PipRequirements:
1636
freeze_output = self._check_output("freeze").strip().splitlines()

src/ewoks/_requirements/managers/pipenv.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import importlib.metadata
12
import json
3+
import os
4+
import sys
5+
from typing import Optional
26

37
from ..metadata.gather import gather_requirements
48
from ..models.pipenv import PipenvRequirements
@@ -7,6 +11,23 @@
711

812
class PipenvManager(BaseManager):
913
NAME = "pipenv"
14+
PRIORITY = 3
15+
16+
def __init__(self, *command: str) -> None:
17+
if not command:
18+
command = sys.executable, "-m", "pipenv"
19+
super().__init__(*command)
20+
21+
def version(self) -> Optional[str]:
22+
"""Returns None when this manager is not available."""
23+
try:
24+
return importlib.metadata.version("pipenv")
25+
except importlib.metadata.PackageNotFoundError:
26+
return None
27+
28+
def is_active(self) -> bool:
29+
"""Manager is explicitly active."""
30+
return "PIPENV_ACTIVE" in os.environ
1031

1132
def _gather_requirements(self, manager_version: str) -> PipenvRequirements:
1233
output = self._check_output("lock", "--requirements")

src/ewoks/_requirements/managers/pixi.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
from typing import Optional
23

34
from ..metadata.gather import gather_requirements
45
from ..models.pixi import PixiRequirements
@@ -7,6 +8,24 @@
78

89
class PixiManager(BaseManager):
910
NAME = "pixi"
11+
PRIORITY = 5
12+
13+
def __init__(self, *command: str) -> None:
14+
if not command:
15+
command = ("pixi",)
16+
super().__init__(*command)
17+
18+
def version(self) -> Optional[str]:
19+
"""Returns None when this manager is not available."""
20+
try:
21+
output = self._check_output("--version", text=True)
22+
return output.strip().split(" ")[-1]
23+
except Exception:
24+
return None
25+
26+
def is_active(self) -> bool:
27+
"""Manager is explicitly active."""
28+
return "PIXI_PROJECT_ROOT" in os.environ
1029

1130
def _gather_requirements(self, manager_version: str) -> PixiRequirements:
1231
if os.path.exists("pixi.lock"):

src/ewoks/_requirements/managers/poetry.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
1+
import importlib.metadata
2+
import os
3+
import sys
4+
from typing import Optional
5+
16
from ..metadata.gather import gather_requirements
27
from ..models.poetry import PoetryRequirements
38
from .utils.base import BaseManager
49

510

611
class PoetryManager(BaseManager):
712
NAME = "poetry"
13+
PRIORITY = 2
14+
15+
def __init__(self, *command: str) -> None:
16+
if not command:
17+
command = sys.executable, "-m", "poetry"
18+
super().__init__(*command)
19+
20+
def version(self) -> Optional[str]:
21+
"""Returns None when this manager is not available."""
22+
try:
23+
return importlib.metadata.version("poetry")
24+
except importlib.metadata.PackageNotFoundError:
25+
return None
26+
27+
def is_active(self) -> bool:
28+
"""Manager is explicitly active."""
29+
return "POETRY_ACTIVE" in os.environ
830

931
def _gather_requirements(self, manager_version: str) -> PoetryRequirements:
1032
output = self._check_output("export", "--without-hashes")

src/ewoks/_requirements/managers/utils/base.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
from abc import abstractmethod
66
from contextlib import contextmanager
77
from typing import Generator
8-
from typing import List
98
from typing import Optional
109

1110
from ...models.base import BaseRequirements
12-
from .commands import get_manager_command
1311

1412
logger = logging.getLogger(__name__)
1513

@@ -34,18 +32,17 @@ class BaseManager:
3432
"""
3533

3634
NAME = NotImplemented
35+
PRIORITY = NotImplemented
3736

3837
def __init__(self, *command: str) -> None:
3938
if not command:
40-
command = get_manager_command(self.NAME)
39+
raise ValueError(f"{type(self).__name__} needs an associated shell command")
4140
self._cmd_args = command
4241

4342
def gather_requirements(self) -> Optional[BaseRequirements]:
4443
"""Return requirements generated from the current python environment."""
45-
from .supported import get_supported_managers
46-
47-
manager_version = get_supported_managers()[self.NAME].version
48-
if not manager_version:
44+
manager_version = self.current_version()
45+
if manager_version is None:
4946
raise RuntimeError(f"{self.NAME!r} is not installed")
5047

5148
try:
@@ -56,6 +53,16 @@ def gather_requirements(self) -> Optional[BaseRequirements]:
5653
)
5754
return None
5855

56+
@abstractmethod
57+
def version(self) -> Optional[str]:
58+
"""Returns None when this manager is not available."""
59+
pass
60+
61+
@abstractmethod
62+
def is_active(self) -> bool:
63+
"""Manager is explicitly active."""
64+
pass
65+
5966
def install_requirements(self, requirements: BaseRequirements) -> None:
6067
"""Install requirements into the current python environment."""
6168
try:
@@ -74,13 +81,25 @@ def _gather_requirements(self, manager_version: str) -> BaseRequirements:
7481
def _install_requirements(self, requirements: BaseRequirements) -> None:
7582
pass
7683

77-
def _check_output(self, *args) -> str:
78-
return _check_output([*self._cmd_args, *args])
84+
def _check_output(self, *args: str) -> str:
85+
return self._check_output_raw(*[*self._cmd_args, *args])
7986

80-
def _check_call(self, *args, raw: bool = False) -> int:
81-
if raw:
82-
return _check_call([*args])
83-
return _check_call([*self._cmd_args, *args])
87+
def _check_call(self, *args: str) -> int:
88+
return self._check_call_raw(*[*self._cmd_args, *args])
89+
90+
@staticmethod
91+
def _check_output_raw(*args: str) -> str:
92+
try:
93+
return subprocess.check_output(args, text=True)
94+
except Exception as ex:
95+
raise RuntimeError(f"Command failed: {args}") from ex
96+
97+
@staticmethod
98+
def _check_call_raw(*args: str) -> int:
99+
try:
100+
return subprocess.check_call(args)
101+
except Exception as ex:
102+
raise RuntimeError(f"Command failed: {args}") from ex
84103

85104
@contextmanager
86105
def _temporary_file(self, text: str, suffix: str) -> Generator[str, None, None]:
@@ -98,17 +117,3 @@ def _temporary_file(self, text: str, suffix: str) -> Generator[str, None, None]:
98117
os.remove(tmp_path)
99118
except OSError:
100119
logger.debug("Could not delete temporary file: %s", tmp_path)
101-
102-
103-
def _check_output(args: List[str]) -> str:
104-
try:
105-
return subprocess.check_output(args, text=True)
106-
except Exception as ex:
107-
raise RuntimeError(f"Command failed: {args}") from ex
108-
109-
110-
def _check_call(args: List[str]) -> int:
111-
try:
112-
return subprocess.check_call(args)
113-
except Exception as ex:
114-
raise RuntimeError(f"Command failed: {args}") from ex

src/ewoks/_requirements/managers/utils/commands.py

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)