| """Implementation of packaging-related magic functions. |
| """ |
| |
| |
| |
| |
| |
| |
| |
|
|
| import functools |
| import os |
| import re |
| import shlex |
| import sys |
| from pathlib import Path |
|
|
| from IPython.core.magic import Magics, magics_class, line_magic |
|
|
|
|
| def is_conda_environment(func): |
| @functools.wraps(func) |
| def wrapper(*args, **kwargs): |
| """Return True if the current Python executable is in a conda env""" |
| |
| if not Path(sys.prefix, "conda-meta", "history").exists(): |
| raise ValueError( |
| "The python kernel does not appear to be a conda environment. " |
| "Please use ``%pip install`` instead." |
| ) |
| return func(*args, **kwargs) |
|
|
| return wrapper |
|
|
|
|
| def _get_conda_like_executable(command): |
| """Find the path to the given executable |
| |
| Parameters |
| ---------- |
| |
| executable: string |
| Value should be: conda, mamba or micromamba |
| """ |
| |
| |
| base_executable = "CONDA_EXE" |
| if "mamba" in command.lower(): |
| base_executable = "MAMBA_EXE" |
| if base_executable in os.environ: |
| executable = Path(os.environ[base_executable]) |
| if executable.is_file(): |
| return str(executable.resolve()) |
|
|
| |
| |
| executable = Path(sys.executable).parent / command |
| if executable.is_file(): |
| return str(executable) |
|
|
| |
| |
| |
| |
| history = Path(sys.prefix, "conda-meta", "history").read_text(encoding="utf-8") |
| match = re.search( |
| rf"^#\s*cmd:\s*(?P<command>.*{command})\s[create|install]", |
| history, |
| flags=re.MULTILINE, |
| ) |
| if match: |
| return match.groupdict()["command"] |
|
|
| |
| return command |
|
|
|
|
| CONDA_COMMANDS_REQUIRING_PREFIX = { |
| 'install', 'list', 'remove', 'uninstall', 'update', 'upgrade', |
| } |
| CONDA_COMMANDS_REQUIRING_YES = { |
| 'install', 'remove', 'uninstall', 'update', 'upgrade', |
| } |
| CONDA_ENV_FLAGS = {'-p', '--prefix', '-n', '--name'} |
| CONDA_YES_FLAGS = {'-y', '--y'} |
|
|
|
|
| @magics_class |
| class PackagingMagics(Magics): |
| """Magics related to packaging & installation""" |
|
|
| @line_magic |
| def pip(self, line): |
| """Run the pip package manager within the current kernel. |
| |
| Usage: |
| %pip install [pkgs] |
| """ |
| python = sys.executable |
| if sys.platform == "win32": |
| python = '"' + python + '"' |
| else: |
| python = shlex.quote(python) |
|
|
| self.shell.system(" ".join([python, "-m", "pip", line])) |
|
|
| print("Note: you may need to restart the kernel to use updated packages.") |
|
|
| def _run_command(self, cmd, line): |
| args = shlex.split(line) |
| command = args[0] if len(args) > 0 else "" |
| args = args[1:] if len(args) > 1 else [""] |
|
|
| extra_args = [] |
|
|
| |
| |
| stdin_disabled = getattr(self.shell, 'kernel', None) is not None |
| needs_yes = command in CONDA_COMMANDS_REQUIRING_YES |
| has_yes = set(args).intersection(CONDA_YES_FLAGS) |
| if stdin_disabled and needs_yes and not has_yes: |
| extra_args.append("--yes") |
|
|
| |
| needs_prefix = command in CONDA_COMMANDS_REQUIRING_PREFIX |
| has_prefix = set(args).intersection(CONDA_ENV_FLAGS) |
| if needs_prefix and not has_prefix: |
| extra_args.extend(["--prefix", sys.prefix]) |
|
|
| self.shell.system(" ".join([cmd, command] + extra_args + args)) |
| print("\nNote: you may need to restart the kernel to use updated packages.") |
|
|
| @line_magic |
| @is_conda_environment |
| def conda(self, line): |
| """Run the conda package manager within the current kernel. |
| |
| Usage: |
| %conda install [pkgs] |
| """ |
| conda = _get_conda_like_executable("conda") |
| self._run_command(conda, line) |
|
|
| @line_magic |
| @is_conda_environment |
| def mamba(self, line): |
| """Run the mamba package manager within the current kernel. |
| |
| Usage: |
| %mamba install [pkgs] |
| """ |
| mamba = _get_conda_like_executable("mamba") |
| self._run_command(mamba, line) |
|
|
| @line_magic |
| @is_conda_environment |
| def micromamba(self, line): |
| """Run the conda package manager within the current kernel. |
| |
| Usage: |
| %micromamba install [pkgs] |
| """ |
| micromamba = _get_conda_like_executable("micromamba") |
| self._run_command(micromamba, line) |
|
|
| @line_magic |
| def uv(self, line): |
| """Run the uv package manager within the current kernel. |
| |
| Usage: |
| %uv pip install [pkgs] |
| """ |
| python = sys.executable |
| if sys.platform == "win32": |
| python = '"' + python + '"' |
| else: |
| python = shlex.quote(python) |
|
|
| self.shell.system(" ".join([python, "-m", "uv", line])) |
|
|
| print("Note: you may need to restart the kernel to use updated packages.") |
|
|