meta_package_manager.shell_env module¶

Import of the login shell environment into a run started outside a terminal.

A process started by a desktop host or a scheduler inherits the environment of that host, not the one a terminal builds. launchd hands a macOS application PATH=/usr/bin:/bin:/usr/sbin:/sbin, the systemd user session behind GNOME Shell never reads .bashrc or .zshrc, and cron gives its jobs /usr/bin:/bin. A manager installed under the home directory (~/.cargo/bin, ~/.local/bin, ~/Library/pnpm/bin) is then invisible to the pool, and pnpm refuses every --global command until its bin directory is on PATH. The menu bar plugin and the GNOME Shell extension both start mpm that way.

The --shell-env option answers this the way VS Code and Emacs do: run the user’s login shell once, as an interactive login shell so both the profile and the rc file are read, ask it for its exported environment, and adopt that environment before any manager is probed. The shell’s own bookkeeping variables are left alone (see SHELL_ENV_KEPT), and the shell has SHELL_ENV_TIMEOUT seconds to answer before the run continues with the environment it started with.

Note

Windows never needs this: a program started from the desktop there already gets the user’s PATH from the registry. The option is accepted and ignored.

Caution

A login shell exports tokens and secrets like any other variable. The answer is read with /usr/bin/env -0, kept in memory, and never logged: only the names of the variables that changed are narrated, at DEBUG.

meta_package_manager.shell_env.SHELL_ENV_GUARD: Final = 'MPM_RESOLVING_SHELL_ENV'¶

Set to 1 in the environment of the shell answering the probe.

A shell configuration can read it to skip a slow or interactive step, the way VSCODE_RESOLVING_ENVIRONMENT serves VS Code. It is also the recursion guard: a configuration that itself runs mpm --shell-env would spawn a shell from within the shell, so import_shell_env() returns at once when it finds the variable already set.

meta_package_manager.shell_env.SHELL_ENV_KEPT: Final[frozenset[str]] = frozenset({'MPM_RESOLVING_SHELL_ENV', 'OLDPWD', 'PWD', 'SHLVL', '_'})¶

Variables the shell’s answer never overwrites.

PWD, OLDPWD, SHLVL and _ describe the shell that answered, not the run that asked: adopting them would place mpm in a directory it is not in. The guard is kept out so it does not stick to the run once the probe is over.

meta_package_manager.shell_env.SHELL_ENV_TIMEOUT: Final = 10¶

Seconds the login shell gets to answer, VS Code’s own budget.

An interactive shell runs the whole rc file, plugin managers and prompt included: half a second is typical, a plugin manager installing itself on first run is not. Past the budget the shell is killed with its process group and the run keeps the environment it started with.

exception meta_package_manager.shell_env.ShellEnvError[source]¶

Bases: Exception

The login shell gave no usable answer.

meta_package_manager.shell_env.shell_argv(shell, mark)[source]¶

Argv running shell as an interactive login shell around the dump command.

Follows VS Code’s shellEnv.ts: -i -l -c for every Bourne-family shell, fish and nushell included, -ic for the C shells, whose -l has to be the only flag, and -Login -Command for PowerShell. The family is read off extra-platforms’ shell catalog, keyed by the file name of the resolved path, so /bin/sh linking to bash is treated as bash.

Todo

Match the file name through extra_platforms.shell_from_path() and drop the pwsh special case once the floor reaches the extra-platforms release carrying kdeldycke/extra-platforms@9a9393d: the catalog keys PowerShell by powershell, where its binary is pwsh on every platform since 6.0.

Return type:

tuple[str, ...]

meta_package_manager.shell_env.read_shell_env(shell, environ, timeout=10)[source]¶

Run shell and return the environment it exports.

The shell starts with environ plus SHELL_ENV_GUARD, in a session of its own: an interactive shell would otherwise claim the terminal mpm runs in, and a timeout has to reap whatever the rc file left running.

Raises:

ShellEnvError – when the shell cannot start, exits with an error, does not answer within timeout seconds, or prints no environment between the markers.

Return type:

dict[str, str]

meta_package_manager.shell_env.import_shell_env(environ=None, timeout=10)[source]¶

Adopt the login shell’s exported environment into environ.

environ defaults to os.environ, which every later PATH lookup and subprocess reads. The shell is the one SHELL names, else the user’s login shell from the passwd database.

Returns the variables that changed, or None when nothing was imported: on Windows, inside the shell answering a probe, when no shell is known, or when the shell gave no usable answer. Each case is narrated, the last two as a warning, since the user asked for the import and did not get it.

Return type:

dict[str, str] | None