##// END OF EJS Templates
Add uv line magic (#14537)...
M Bussonnier -
r28906:02545e95 merge
parent child Browse files
Show More
@@ -1,164 +1,181
1 1 """Implementation of packaging-related magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2018 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 import functools
12 12 import os
13 13 import re
14 14 import shlex
15 15 import sys
16 16 from pathlib import Path
17 17
18 18 from IPython.core.magic import Magics, magics_class, line_magic
19 19
20 20
21 21 def is_conda_environment(func):
22 22 @functools.wraps(func)
23 23 def wrapper(*args, **kwargs):
24 24 """Return True if the current Python executable is in a conda env"""
25 25 # TODO: does this need to change on windows?
26 26 if not Path(sys.prefix, "conda-meta", "history").exists():
27 27 raise ValueError(
28 28 "The python kernel does not appear to be a conda environment. "
29 29 "Please use ``%pip install`` instead."
30 30 )
31 31 return func(*args, **kwargs)
32 32
33 33 return wrapper
34 34
35 35
36 36 def _get_conda_like_executable(command):
37 37 """Find the path to the given executable
38 38
39 39 Parameters
40 40 ----------
41 41
42 42 executable: string
43 43 Value should be: conda, mamba or micromamba
44 44 """
45 45 # Check for a environment variable bound to the base executable, both conda and mamba
46 46 # set these when activating an environment.
47 47 base_executable = "CONDA_EXE"
48 48 if "mamba" in command.lower():
49 49 base_executable = "MAMBA_EXE"
50 50 if base_executable in os.environ:
51 51 executable = Path(os.environ[base_executable])
52 52 if executable.is_file():
53 53 return str(executable.resolve())
54 54
55 55 # Check if there is a conda executable in the same directory as the Python executable.
56 56 # This is the case within conda's root environment.
57 57 executable = Path(sys.executable).parent / command
58 58 if executable.is_file():
59 59 return str(executable)
60 60
61 61 # Otherwise, attempt to extract the executable from conda history.
62 62 # This applies in any conda environment. Parsing this way is error prone because
63 63 # different versions of conda and mamba include differing cmd values such as
64 64 # `conda`, `conda-script.py`, or `path/to/conda`, here use the raw command provided.
65 65 history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8")
66 66 match = re.search(
67 67 rf"^#\s*cmd:\s*(?P<command>.*{command})\s[create|install]",
68 68 history,
69 69 flags=re.MULTILINE,
70 70 )
71 71 if match:
72 72 return match.groupdict()["command"]
73 73
74 74 # Fallback: assume the executable is available on the system path.
75 75 return command
76 76
77 77
78 78 CONDA_COMMANDS_REQUIRING_PREFIX = {
79 79 'install', 'list', 'remove', 'uninstall', 'update', 'upgrade',
80 80 }
81 81 CONDA_COMMANDS_REQUIRING_YES = {
82 82 'install', 'remove', 'uninstall', 'update', 'upgrade',
83 83 }
84 84 CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'}
85 85 CONDA_YES_FLAGS = {'-y', '--y'}
86 86
87 87
88 88 @magics_class
89 89 class PackagingMagics(Magics):
90 90 """Magics related to packaging & installation"""
91 91
92 92 @line_magic
93 93 def pip(self, line):
94 94 """Run the pip package manager within the current kernel.
95 95
96 96 Usage:
97 97 %pip install [pkgs]
98 98 """
99 99 python = sys.executable
100 100 if sys.platform == "win32":
101 101 python = '"' + python + '"'
102 102 else:
103 103 python = shlex.quote(python)
104 104
105 105 self.shell.system(" ".join([python, "-m", "pip", line]))
106 106
107 107 print("Note: you may need to restart the kernel to use updated packages.")
108 108
109 109 def _run_command(self, cmd, line):
110 110 args = shlex.split(line)
111 111 command = args[0] if len(args) > 0 else ""
112 112 args = args[1:] if len(args) > 1 else [""]
113 113
114 114 extra_args = []
115 115
116 116 # When the subprocess does not allow us to respond "yes" during the installation,
117 117 # we need to insert --yes in the argument list for some commands
118 118 stdin_disabled = getattr(self.shell, 'kernel', None) is not None
119 119 needs_yes = command in CONDA_COMMANDS_REQUIRING_YES
120 120 has_yes = set(args).intersection(CONDA_YES_FLAGS)
121 121 if stdin_disabled and needs_yes and not has_yes:
122 122 extra_args.append("--yes")
123 123
124 124 # Add --prefix to point conda installation to the current environment
125 125 needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX
126 126 has_prefix = set(args).intersection(CONDA_ENV_FLAGS)
127 127 if needs_prefix and not has_prefix:
128 128 extra_args.extend(["--prefix", sys.prefix])
129 129
130 130 self.shell.system(" ".join([cmd, command] + extra_args + args))
131 131 print("\nNote: you may need to restart the kernel to use updated packages.")
132 132
133 133 @line_magic
134 134 @is_conda_environment
135 135 def conda(self, line):
136 136 """Run the conda package manager within the current kernel.
137 137
138 138 Usage:
139 139 %conda install [pkgs]
140 140 """
141 141 conda = _get_conda_like_executable("conda")
142 142 self._run_command(conda, line)
143 143
144 144 @line_magic
145 145 @is_conda_environment
146 146 def mamba(self, line):
147 147 """Run the mamba package manager within the current kernel.
148 148
149 149 Usage:
150 150 %mamba install [pkgs]
151 151 """
152 152 mamba = _get_conda_like_executable("mamba")
153 153 self._run_command(mamba, line)
154 154
155 155 @line_magic
156 156 @is_conda_environment
157 157 def micromamba(self, line):
158 158 """Run the conda package manager within the current kernel.
159 159
160 160 Usage:
161 161 %micromamba install [pkgs]
162 162 """
163 163 micromamba = _get_conda_like_executable("micromamba")
164 164 self._run_command(micromamba, line)
165
166 @line_magic
167 def uv(self, line):
168 """Run the uv package manager within the current kernel.
169
170 Usage:
171 %uv pip install [pkgs]
172 """
173 python = sys.executable
174 if sys.platform == "win32":
175 python = '"' + python + '"'
176 else:
177 python = shlex.quote(python)
178
179 self.shell.system(" ".join([python, "-m", "uv", line]))
180
181 print("Note: you may need to restart the kernel to use updated packages.")
General Comments 0
You need to be logged in to leave comments. Login now