##// END OF EJS Templates
Implement magic for mamba and micromamba
Samuel Gaist -
Show More
@@ -1,16 +1,15 b''
1 # See https://pre-commit.com for more information
1 # See https://pre-commit.com for more information
2 # See https://pre-commit.com/hooks.html for more hooks
2 # See https://pre-commit.com/hooks.html for more hooks
3 repos:
3 repos:
4 - repo: https://github.com/pre-commit/pre-commit-hooks
4 - repo: https://github.com/pre-commit/pre-commit-hooks
5 rev: v3.2.0
5 rev: v4.4.0
6 hooks:
6 hooks:
7 - id: trailing-whitespace
7 - id: trailing-whitespace
8 - id: end-of-file-fixer
8 - id: end-of-file-fixer
9 - id: check-yaml
9 - id: check-yaml
10 - id: check-added-large-files
10 - id: check-added-large-files
11
11
12 - repo: https://github.com/akaihola/darker
12 - repo: https://github.com/akaihola/darker
13 rev: 1.3.1
13 rev: 1.7.2
14 hooks:
14 hooks:
15 - id: darker
15 - id: darker
16
@@ -1,112 +1,151 b''
1 """Implementation of packaging-related magic functions.
1 """Implementation of packaging-related magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2018 The IPython Development Team.
4 # Copyright (c) 2018 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 import functools
11 import re
12 import re
12 import shlex
13 import shlex
13 import sys
14 import sys
14 from pathlib import Path
15 from pathlib import Path
15
16
16 from IPython.core.magic import Magics, magics_class, line_magic
17 from IPython.core.magic import Magics, magics_class, line_magic
17
18
18
19
19 def _is_conda_environment():
20 def is_conda_environment(func):
20 """Return True if the current Python executable is in a conda env"""
21 @functools.wraps(func)
21 # TODO: does this need to change on windows?
22 def wrapper(*args, **kwargs):
22 return Path(sys.prefix, "conda-meta", "history").exists()
23 """Return True if the current Python executable is in a conda env"""
24 # TODO: does this need to change on windows?
25 if not Path(sys.prefix, "conda-meta", "history").exists():
26 raise ValueError(
27 "The python kernel does not appear to be a conda environment. "
28 "Please use ``%pip install`` instead."
29 )
30 return func(*args, **kwargs)
23
31
32 return wrapper
24
33
25 def _get_conda_executable():
34
26 """Find the path to the conda executable"""
35 def _get_conda_like_executable(command):
36 """Find the path to the given executable
37
38 Parameters
39 ----------
40
41 executable: string
42 Value should be: conda, mamba or micromamba
43 """
27 # Check if there is a conda executable in the same directory as the Python executable.
44 # Check if there is a conda executable in the same directory as the Python executable.
28 # This is the case within conda's root environment.
45 # This is the case within conda's root environment.
29 conda = Path(sys.executable).parent / "conda"
46 executable = Path(sys.executable).parent / command
30 if conda.is_file():
47 if executable.is_file():
31 return str(conda)
48 return str(executable)
32
49
33 # Otherwise, attempt to extract the executable from conda history.
50 # Otherwise, attempt to extract the executable from conda history.
34 # This applies in any conda environment.
51 # This applies in any conda environment.
35 history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8")
52 history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8")
36 match = re.search(
53 match = re.search(
37 r"^#\s*cmd:\s*(?P<command>.*conda)\s[create|install]",
54 rf"^#\s*cmd:\s*(?P<command>.*{executable})\s[create|install]",
38 history,
55 history,
39 flags=re.MULTILINE,
56 flags=re.MULTILINE,
40 )
57 )
41 if match:
58 if match:
42 return match.groupdict()["command"]
59 return match.groupdict()["command"]
43
60
44 # Fallback: assume conda is available on the system path.
61 # Fallback: assume the executable is available on the system path.
45 return "conda"
62 return command
46
63
47
64
48 CONDA_COMMANDS_REQUIRING_PREFIX = {
65 CONDA_COMMANDS_REQUIRING_PREFIX = {
49 'install', 'list', 'remove', 'uninstall', 'update', 'upgrade',
66 'install', 'list', 'remove', 'uninstall', 'update', 'upgrade',
50 }
67 }
51 CONDA_COMMANDS_REQUIRING_YES = {
68 CONDA_COMMANDS_REQUIRING_YES = {
52 'install', 'remove', 'uninstall', 'update', 'upgrade',
69 'install', 'remove', 'uninstall', 'update', 'upgrade',
53 }
70 }
54 CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'}
71 CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'}
55 CONDA_YES_FLAGS = {'-y', '--y'}
72 CONDA_YES_FLAGS = {'-y', '--y'}
56
73
57
74
58 @magics_class
75 @magics_class
59 class PackagingMagics(Magics):
76 class PackagingMagics(Magics):
60 """Magics related to packaging & installation"""
77 """Magics related to packaging & installation"""
61
78
62 @line_magic
79 @line_magic
63 def pip(self, line):
80 def pip(self, line):
64 """Run the pip package manager within the current kernel.
81 """Run the pip package manager within the current kernel.
65
82
66 Usage:
83 Usage:
67 %pip install [pkgs]
84 %pip install [pkgs]
68 """
85 """
69 python = sys.executable
86 python = sys.executable
70 if sys.platform == "win32":
87 if sys.platform == "win32":
71 python = '"' + python + '"'
88 python = '"' + python + '"'
72 else:
89 else:
73 python = shlex.quote(python)
90 python = shlex.quote(python)
74
91
75 self.shell.system(" ".join([python, "-m", "pip", line]))
92 self.shell.system(" ".join([python, "-m", "pip", line]))
76
93
77 print("Note: you may need to restart the kernel to use updated packages.")
94 print("Note: you may need to restart the kernel to use updated packages.")
78
95
79 @line_magic
96 def _run_command(self, cmd, line):
80 def conda(self, line):
81 """Run the conda package manager within the current kernel.
82
83 Usage:
84 %conda install [pkgs]
85 """
86 if not _is_conda_environment():
87 raise ValueError("The python kernel does not appear to be a conda environment. "
88 "Please use ``%pip install`` instead.")
89
90 conda = _get_conda_executable()
91 args = shlex.split(line)
97 args = shlex.split(line)
92 command = args[0] if len(args) > 0 else ""
98 command = args[0] if len(args) > 0 else ""
93 args = args[1:] if len(args) > 1 else [""]
99 args = args[1:] if len(args) > 1 else [""]
94
100
95 extra_args = []
101 extra_args = []
96
102
97 # When the subprocess does not allow us to respond "yes" during the installation,
103 # When the subprocess does not allow us to respond "yes" during the installation,
98 # we need to insert --yes in the argument list for some commands
104 # we need to insert --yes in the argument list for some commands
99 stdin_disabled = getattr(self.shell, 'kernel', None) is not None
105 stdin_disabled = getattr(self.shell, 'kernel', None) is not None
100 needs_yes = command in CONDA_COMMANDS_REQUIRING_YES
106 needs_yes = command in CONDA_COMMANDS_REQUIRING_YES
101 has_yes = set(args).intersection(CONDA_YES_FLAGS)
107 has_yes = set(args).intersection(CONDA_YES_FLAGS)
102 if stdin_disabled and needs_yes and not has_yes:
108 if stdin_disabled and needs_yes and not has_yes:
103 extra_args.append("--yes")
109 extra_args.append("--yes")
104
110
105 # Add --prefix to point conda installation to the current environment
111 # Add --prefix to point conda installation to the current environment
106 needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX
112 needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX
107 has_prefix = set(args).intersection(CONDA_ENV_FLAGS)
113 has_prefix = set(args).intersection(CONDA_ENV_FLAGS)
108 if needs_prefix and not has_prefix:
114 if needs_prefix and not has_prefix:
109 extra_args.extend(["--prefix", sys.prefix])
115 extra_args.extend(["--prefix", sys.prefix])
110
116
111 self.shell.system(' '.join([conda, command] + extra_args + args))
117 self.shell.system(" ".join([cmd, command] + extra_args + args))
112 print("\nNote: you may need to restart the kernel to use updated packages.")
118 print("\nNote: you may need to restart the kernel to use updated packages.")
119
120 @line_magic
121 @is_conda_environment
122 def conda(self, line):
123 """Run the conda package manager within the current kernel.
124
125 Usage:
126 %conda install [pkgs]
127 """
128 conda = _get_conda_like_executable("conda")
129 self._run_command(conda, line)
130
131 @line_magic
132 @is_conda_environment
133 def mamba(self, line):
134 """Run the mamba package manager within the current kernel.
135
136 Usage:
137 %mamba install [pkgs]
138 """
139 mamba = _get_conda_like_executable("mamba")
140 self._run_command(mamba, line)
141
142 @line_magic
143 @is_conda_environment
144 def micromamba(self, line):
145 """Run the conda package manager within the current kernel.
146
147 Usage:
148 %micromamba install [pkgs]
149 """
150 micromamba = _get_conda_like_executable("micromamba")
151 self._run_command(micromamba, line)
General Comments 0
You need to be logged in to leave comments. Login now