Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/magics/packaging.py: 39%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

84 statements  

1"""Implementation of packaging-related magic functions. 

2""" 

3#----------------------------------------------------------------------------- 

4# Copyright (c) 2018 The IPython Development Team. 

5# 

6# Distributed under the terms of the Modified BSD License. 

7# 

8# The full license is in the file COPYING.txt, distributed with this software. 

9#----------------------------------------------------------------------------- 

10 

11import functools 

12import os 

13import re 

14import shlex 

15import sys 

16from pathlib import Path 

17 

18from IPython.core.magic import Magics, magics_class, line_magic 

19 

20 

21def is_conda_environment(func): 

22 @functools.wraps(func) 

23 def wrapper(*args, **kwargs): 

24 """Return True if the current Python executable is in a conda env""" 

25 # TODO: does this need to change on windows? 

26 if not Path(sys.prefix, "conda-meta", "history").exists(): 

27 raise ValueError( 

28 "The python kernel does not appear to be a conda environment. " 

29 "Please use ``%pip install`` instead." 

30 ) 

31 return func(*args, **kwargs) 

32 

33 return wrapper 

34 

35 

36def _get_conda_like_executable(command): 

37 """Find the path to the given executable 

38 

39 Parameters 

40 ---------- 

41 

42 executable: string 

43 Value should be: conda, mamba or micromamba 

44 """ 

45 # Check for a environment variable bound to the base executable, both conda and mamba 

46 # set these when activating an environment. 

47 base_executable = "CONDA_EXE" 

48 if "mamba" in command.lower(): 

49 base_executable = "MAMBA_EXE" 

50 if base_executable in os.environ: 

51 executable = Path(os.environ[base_executable]) 

52 if executable.is_file(): 

53 return str(executable.resolve()) 

54 

55 # Check if there is a conda executable in the same directory as the Python executable. 

56 # This is the case within conda's root environment. 

57 executable = Path(sys.executable).parent / command 

58 if executable.is_file(): 

59 return str(executable) 

60 

61 # Otherwise, attempt to extract the executable from conda history. 

62 # This applies in any conda environment. Parsing this way is error prone because 

63 # different versions of conda and mamba include differing cmd values such as 

64 # `conda`, `conda-script.py`, or `path/to/conda`, here use the raw command provided. 

65 history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8") 

66 match = re.search( 

67 rf"^#\s*cmd:\s*(?P<command>.*{command})\s[create|install]", 

68 history, 

69 flags=re.MULTILINE, 

70 ) 

71 if match: 

72 return match.groupdict()["command"] 

73 

74 # Fallback: assume the executable is available on the system path. 

75 return command 

76 

77 

78CONDA_COMMANDS_REQUIRING_PREFIX = { 

79 'install', 'list', 'remove', 'uninstall', 'update', 'upgrade', 

80} 

81CONDA_COMMANDS_REQUIRING_YES = { 

82 'install', 'remove', 'uninstall', 'update', 'upgrade', 

83} 

84CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'} 

85CONDA_YES_FLAGS = {'-y', '--y'} 

86 

87 

88@magics_class 

89class PackagingMagics(Magics): 

90 """Magics related to packaging & installation""" 

91 

92 @line_magic 

93 def pip(self, line): 

94 """Run the pip package manager within the current kernel. 

95 

96 Usage: 

97 %pip install [pkgs] 

98 """ 

99 python = sys.executable 

100 if sys.platform == "win32": 

101 python = '"' + python + '"' 

102 else: 

103 python = shlex.quote(python) 

104 

105 self.shell.system(" ".join([python, "-m", "pip", line])) 

106 

107 print("Note: you may need to restart the kernel to use updated packages.") 

108 

109 def _run_command(self, cmd, line): 

110 args = shlex.split(line) 

111 command = args[0] if len(args) > 0 else "" 

112 args = args[1:] if len(args) > 1 else [""] 

113 

114 extra_args = [] 

115 

116 # When the subprocess does not allow us to respond "yes" during the installation, 

117 # we need to insert --yes in the argument list for some commands 

118 stdin_disabled = getattr(self.shell, 'kernel', None) is not None 

119 needs_yes = command in CONDA_COMMANDS_REQUIRING_YES 

120 has_yes = set(args).intersection(CONDA_YES_FLAGS) 

121 if stdin_disabled and needs_yes and not has_yes: 

122 extra_args.append("--yes") 

123 

124 # Add --prefix to point conda installation to the current environment 

125 needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX 

126 has_prefix = set(args).intersection(CONDA_ENV_FLAGS) 

127 if needs_prefix and not has_prefix: 

128 extra_args.extend(["--prefix", sys.prefix]) 

129 

130 self.shell.system(" ".join([cmd, command] + extra_args + args)) 

131 print("\nNote: you may need to restart the kernel to use updated packages.") 

132 

133 @line_magic 

134 @is_conda_environment 

135 def conda(self, line): 

136 """Run the conda package manager within the current kernel. 

137 

138 Usage: 

139 %conda install [pkgs] 

140 """ 

141 conda = _get_conda_like_executable("conda") 

142 self._run_command(conda, line) 

143 

144 @line_magic 

145 @is_conda_environment 

146 def mamba(self, line): 

147 """Run the mamba package manager within the current kernel. 

148 

149 Usage: 

150 %mamba install [pkgs] 

151 """ 

152 mamba = _get_conda_like_executable("mamba") 

153 self._run_command(mamba, line) 

154 

155 @line_magic 

156 @is_conda_environment 

157 def micromamba(self, line): 

158 """Run the conda package manager within the current kernel. 

159 

160 Usage: 

161 %micromamba install [pkgs] 

162 """ 

163 micromamba = _get_conda_like_executable("micromamba") 

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.")